Download presentation
Presentation is loading. Please wait.
Published byLucia Greenway Modified over 9 years ago
1
Fixing Software Before It Breaks SIGAda 2001 S. Tucker Taft CTO AverCom Corp., a Titan Company October 3, 2001 Bloomington, MN
2
Outline Why Static Analysis? Comparison of Static Error Detection in Current Ada versus C-based Languages How Far Can Static Analysis Go? Possible New (Annotation) Language Features to Enhance Static Error Detection Conclusion Acknowledgements: These ideas have been freely stolen from SPARK, Eiffel, Cyclone, Hermes/NIL,... and further developed in conjunction with Bob Duff
3
Why Static Analysis? Full Run-Time Coverage Testing is Very Expensive If Run-Time Exceptions are Possible, Coverage Testing Becomes That Much Harder Tasking Creates A Potentially Intractable Number of Distinct Cases to Test Generics Can Only Be Tested At Run-Time for Specific Instantiations -- Testing in General At Run-Time Is Not Defined.
4
Comparing Current Ada with C-Based Languages Array Checking Ada: Must be correct index type C/C++/Java: Any integer type may index any array; Java checks out-of-range at run-time Enumeration Types Ada: No implicit conversion to/from an enumeration type C: Freely interconvertible with any integer type C++: Freely convertible to integer, but not back Java: No enumeration types, must use ints with named constants
5
Comparing Current Ada with C-Based Languages (cont’d) Generic Templates Ada: Compile-time checks that generic body uses parameters appropriately C++: No checking of generic template until instantiated Synchronized/Protected Operations Ada: All operations with access to protected data components lock during call Java: May always add operation that doesn’t lock, either in same class or a subclass
6
Comparing Current Ada with C-Based Languages (cont’d) Reading Uninitialized Variables Ada: Bounded error to reference Java: Disallowed at Compile-Time Exceptions Handled Ada: Unhandled exceptions propagated Java: Unhandled exceptions disallowed at compile-time unless in “throws” clause (or “unchecked” exception)
7
How Far Can Static Analysis Go? My Claim: All situations that might result in failures of language-defined checks, or in bounded error or erroneous execution, can be eliminated at compile-time My Concern: How conservative, burdensome, or restrictive would the compile-time checks become? SPARK-Ada95 is example of fairly restrictive subset, though it has grown into a highly usable system.
8
How Far Can Static Analysis Go? (cont’d) My Belief: Careful (annotation) language design can produce a highly flexible and usable language which nevertheless has no language-defined run-time failure conditions. Many Higher-level or Application-specific failure conditions or inconsistencies can also be eliminated at compile-time using a “programmable” annotation language Physical Units Checking is an example Need to Study: What are Typical Sources of (Human) Error
9
Typical Mistakes Sin of Omission Uninitialized Variables Missing case in switch/case statement Missing token/character (e.g. “break;”, “=“, “&”) Missing return statement Missing “increment” step in loop Sin of Confusion Swapping the parameters to “strcpy” Confusing 1 = success vs. 0 = success Forgetting about implicit effects or precedence
10
Typical Mistakes (cont’d) Sin of Short-Sightedness (aka Laziness) Fixed size buffer (the famous “gets” hole in “sendmail”) Fixed size table (annoying “too many revisions” in RCS) Well (Human) Engineered Language can: Minimize Occurrence of These, or... Catch These Mistakes at Compile-Time Well Human Engineered
11
Blaming the Worker for C’s Poor Human Engineering A Software Fault Prevention Approach in Coding and Root Cause Analysis Weider D. Yu, Bell Labs Technical Journal, April-June 1998 The top three root causes of the faults were: execution/oversight (38%), Inadequate attention to details (75%) and inadequate considerations to all relevant issues (11%); resource/planning (19%), Not enough engineer time (76%) and not enough internal support (4%); education and training (15%). Area of technical responsibility (68%) and programming language usage (15%).
12
Lucent 5ESS Software Fault Root Cause Analysis No Mention of Defective Material (i.e. the C Language) -- Only Defective Workmanship!
13
Lucent 5ESS C Programmer Advisories Initialize all variables before use Control flow of break and continue statements Check C operator associativity and precedence for correct usage Ensure loop boundaries are correct Do not over-index arrays Ensure value of variables is not truncated Reference pointer variables correctly Check pointer increments/decrements Ensure logical OR and AND tests are correct
14
Lucent 5ESS C Programmer Advisories (cont’d) Use all assignment and equal operators as intended Ensure bit field data types are either unsigned or enum Use logical AND and mask operators as intended Check preprocessor conditionals Check comment delimiters Test unsigned variables for == 0 or != 0 only Use cast cautiously Such Advisories are a sure sign of Poor Human Engineering and/or Defective Materials
15
The Big Trick to Catching Errors Early Redundancy Force the programmer to follow the writing- teacher’s rule: Tell you what they are going to say Tell you Tell you what they said Kinds of Redundancy: Type Declarations Subrange Declarations var vs. const, in vs. out specification Assertions, Pre/Post Conditions Separation of Interface and Implementation Tools should check for consistency
16
The Second Big Trick Compartmentalization Create many different specialized types Any one type may only be used in appropriate places Examples of Compartmentalization: Use Uniquely shaped plugs and sockets Define Appropriate Enumeration Types Disallow Implicit Numeric Conversions, Allow usage- specific numeric types (“age”, “length”) Specify Minimal Subrange for each variable Require Boolean inside a conditional test Avoid “if (func_name) { …” Create a “gauntlet” for the program to pass Lot’s of little, picky, tests on every line (like a slalom)
17
The Third Big Trick Require Completeness Don’t Fill in the blanks without explicit direction (e.g. explicit and local specification of default) Examples of Completeness Requirements: Case statement must cover all possible values of expression Initializer must provide value for all components Implementation Module must provide code for every (non-abstract) function appearing in Interface Module; every abstract function must be overridden All Local Variables must be Explicitly Initialized on all paths prior to use Every path out of function requires a “return” or “raise”
18
The Last Big Trick Require Explicitness aka: “Say it at least once!” Favor the reader over the writer Minimize implicit/side effects Keep semantics obvious Disallow possibly ambiguous cases E.g., don’t create elaborate preference rules or implicit conversion rules Make sure any defaults are safe and benign Make sure the simple thing to do is the safest and easiest to maintain Make sure unsafe programming constructs are highly visible, and require extra and explicit work at each use X
19
Examples of Areas for Enhanced Static Analysis for Ada Uninitialized Variables Null Checks Erroneous Concurrent Access Access-Before Elaboration Physical Units Checking Programmable Static Analysis
20
Basic Principles All Run-Time Checks become Compile-Time Checks based on possibility of failure using “basic” flow analysis Should support “simple” conditional/variant code E.g.: if cond then X := 3; end if; …. If cond then Y := X; end if; Explicit Checks coded by programmer will be recognized E.g.: if X /= null then... Annotations added to Spec to “Pass” PreConditions/PostConditions to Caller This process can be automated
21
Uninitialized Variables Add in/out/in-out/uninit annotation for global variables to spec Add init/uninit annotations for exception propagation (if exceptions allowed) Add default init/uninit annotations for access/comp/priv types (normal default is init) Require initialization for: Elementary value usage RHS of composite assignment “in”, “in out” params/globals at call (including ops) Expr or “out” param/global at return
22
Null Checks Very similar to Uninitialized variables Add “not-null” annotation (or subtype constraint) for params, globals, func-result Add “not-null” annotation for components Require not null value upon: Dereference (implicit or explicit) Pass/Assign to not-null param/object Return with not-null result, “out” param/global
23
Erroneous Concurrent Access Add “task_safe” annotation for subprograms, packages, tasks Task_safe subprograms may only refer to globals that are atomic or protected. Task_safe tasks/subps may only have nested task_safe tasks, and call non-local subprograms only if they are task_safe. “Task_safe” on a package implies all nested tasks, and all visible task types and subps are task_safe, and has task_safe elaboration
24
Access Before Elaboration Add pragma which requires static elaboration checks Complain at link-time if statically-safe elaboration order not determinable Analysis shows this imposes very few restrictions on “real” programs
25
Physical Units Checking Add pragma/attribute/annotation of units for scalar subtypes Add subp annotations that require unit relationships, or compute units of result/out params based on units of other params Compute and check unit relationships at compile-time or instantiation time.
26
Programmable Static Analysis Allow declaration of “compile-time” attributes for instances of types, instances of generic, etc. Allow annotations / preconditions / postconditions to use compile-time attributes. Propagate annotations along with other information through flow analysis
27
Conclusion Proving correctness is extremely hard Proving “reasonableness,” consistency, etc. is feasible at compile-time Pushing all of Ada’s current exception, bounded-error, and erroneous conditions to compile-time checks is possible, without creating excessive constraints Adding programmable static analysis can allow compile-time checking to check application-specific “reasonableness.”
Similar presentations
© 2024 SlidePlayer.com Inc.
All rights reserved.