Top 10 Bug-Killing Coding Standard Rules Michael Barr & Dan Smith Webinar: June 3, 2014 MICHAEL BARR, CTO Electrical Engineer (BSEE/MSEE) Experienced Embedded Software Developer Consultant & Trainer (1999-present) Embedded software process and architecture improvement Various industries (e.g., medical devices, industrial controls) Former Adjunct Professor University of Maryland 2000-2003 (Design and Use of Operating Systems) Johns Hopkins University 2012 (Embedded Software Architecture) Served as Editor-in-Chief, Columnist, Conference Chair Expert witness (e.g., in re: Toyota unintended acceleration) Author of 3 books and over 70 articles/papers 2 Copyright Barr Group, LLC. Page 1
BOOK: EMBEDDED C CODING STANDARD Bugs are expensive to find/kill It s cheaper/easier to keep a bug out Bias toward checkable bug-killers Goals: Safety, security, and reliability These 10 bug-killing rules + more Complementary to MISRA-C guidelines Including our internal stylistic rules http://barrgroup.com/coding-standard 3 BARR GROUP The Embedded Systems Experts Barr Group helps companies make their embedded systems safer and more secure. BARRGROUP.COM 4 Copyright Barr Group, LLC. Page 2
UPCOMING PUBLIC TRAININGS Embedded SOFTWARE Boot Camp October 20-24 in Detroit, Michigan Embedded ANDROID Boot Camp October 27-31 in Costa Mesa, California Embedded SECURITY Boot Camp November 3-7 in Germantown, Maryland http://barrgroup.com/training-calendar 5 OVERVIEW OF TODAY S WEBINAR Goal Establish that simple coding rules can reduce bugs Key Takeaways 10 easy-to-follow bug-killing rules for coding standards Prerequisites Familiarity with embedded programming in C or C++ 6 Copyright Barr Group, LLC. Page 3
DAN SMITH, PRINCIPAL ENGINEER BSEE (comp.eng), Princeton Experienced firmware developer 20+ years of embedded systems design Control systems, telecom/datacom, medical devices, defense, transportation Numerous RTOSes, processors, platforms Engineer, instructor, speaker, consultant Focus on secure, safe, fault-tolerant systems webinars@barrgroup.com 7 WHY ADOPT A CODING STANDARD? Several good reasons engineers talk about Readability, when all of the code looks a certain way This results when there is a focus on stylistic rules Portability, when C s inconsistencies are managed An even better reason is not talked about enough Certain coding standard rules can keep bugs out! Finding bugs is hard and time-consuming 8 Copyright Barr Group, LLC. Page 4
WHERE BUGS COME FROM The original programmer(s) Programming is the step of bugging the code Maintenance programmer(s) By breaking assumptions of the original programmer By misunderstanding the original programmer s intent Both types of bugs can be reduced by following a set of bug-reducing coding rules! 9 MISRA-C GUIDELINES Guidelines for use of the C language in critical systems A carefully-rationalized subset of C We highly recommend following it But MISRA-C is a set of guidelines NOT a coding standard Style is not addressed at all Barr Group s standard is compatible with MISRA-C 10 Copyright Barr Group, LLC. Page 5
CODING STANDARD ENFORCEMENT To be effective, coding standards must be both: ENFORCEABLE Favor objective, enforceable rules over unenforceable ones Enforce automatically with tools whenever possible And ENFORCED By the entire team, and its standard processes Few, if any, exceptions allowed (and documented) 11 Rule #1 always use braces Rule: Braces ({ }) shall always surround the blocks of code (also known as compound statements) following if, else, switch, while, do, and for keywords. Single statements and empty statements following these keywords shall also always be surrounded by braces. 12 Copyright Barr Group, LLC. Page 6
ALWAYS-BRACES EXAMPLE CODE DON T if (5 == foo) bar(); // Indenting not sufficient; use braces also. always_run(); if (5 == foo) bar(); always_run(); // Will be executed only when foo == 5!! if (5 == foo) bar(); fred(); always_run(); // Will always be executed 13 ALWAYS-BRACES EXAMPLE CODE DO DO if (5 == foo) { // All code inside the braces conditionally executed. bar(); } always_run(); while (!timer.b_done) { // Even an empty statement should have braces. } 14 Copyright Barr Group, LLC. Page 7
Rule #2 whenever possible const Rule: The const keyword shall be used whenever possible, including: To declare variables that should not be changed after initialization To define call-by-reference function parameters that should not be modified To define fields in structs and unions that cannot be modified (e.g., in a struct overlay for memory-mapped I/O register) As a strongly typed alternative to #define for numerical constants 15 MAXIMIZE-CONST EXAMPLE CODE DO // Variables that won t be changed & can be stored in ROM. char const * gp_model_name = Acme 9000 ; int const g_build_number = CURRENT_BUILD_NUMBER(); // Function parameters that must not be modified. char * strncpy(char * p_dest, char const * p_src, size_t count) { } // Strongly-typed alternative to #define. size_t const HEAP_SIZE = 8192; 16 Copyright Barr Group, LLC. Page 8
Rule #3 whenever possible static Rule: The static keyword shall be used to declare all functions and variables that do not need to be visible outside of the module in which they are declared. Increases encapsulation and data protection Protects long-lived data from cross-module access Reduces coupling, improves maintainability 17 MAXIMIZE-STATIC EXAMPLE CODE DO (timer.c) #include timer.h // Variable with visibility only within this file. static uint32_t g_next_timeout = 0; // Helper function not callable from outside this file. static uint32_t add_timer_to_active_list( ) { ++g_next_timeout = ; } 18 Copyright Barr Group, LLC. Page 9
Rule #4 whenever necessary volatile Rule: The volatile keyword shall be used whenever appropriate, including: To declare a global variable accessible (by current use or scope) by any interrupt service routine To declare a global variable accessible (by current use or scope) by two or more tasks To declare a pointer to a memory-mapped I/O peripheral register set Eliminates a whole class of difficult bugs! 19 OPTIMIZATION: REDUNDANT READS What your code says timer.count = 0; timer.control = 0xE000; while (timer.count < 100) { // do stuff } // do more stuff What the optimizer does timer.count = 0; timer.control = 0xE000; while (1) { // do stuff } [saves time and code space] 20 Copyright Barr Group, LLC. Page 10
OPTIMIZATION: UNNECESSARY WRITES What your code says led_reg = 0xFE; // LED0: patient dying // code that never reads led_reg led_reg = 0xFF; // LED0: patient stable What the optimizer does [saves time and code space] // code that never reads led_reg led_reg = 0xFF; // LED0: patient stable 21 MORE ON VOLATILE If working code fails first at optimizer enable missing volatile keywords are the likely culprits Solution: review all declarations for missing volatile volatile rarely used outside embedded software Great question to ask prospective firmware hires! Other uses of volatile 22 Data that must be wiped (e.g., plaintext, keys, etc.) Sequencing of operations Copyright Barr Group, LLC. Page 11
VOLATILE USAGE - EXAMPLE CODE DO // Every shared global object is volatile uint16_t volatile g_state = SYSTEM_STARTUP; // as is every hardware register. typedef struct { } my_fpga_t; my_fpga_t volatile * const p_timer = ; 23 // Even some types of non-shared data should be uint8_t volatile plaintext[max_plaintext]; Rule #5 don t disable code with comments Rule: Comments shall not be used to disable a block of code, even temporarily. Use the preprocessor s conditional compilation feature. Nested comments not part of C standard Supported on some compilers, not on others Use version control for experimental code changes Don t leave commented out code for next developer 24 Copyright Barr Group, LLC. Page 12
COMMENTED-OUT CODE EXAMPLE DON T DO /* outer comment a = a + 1; /* nested comment */ b = b + 1; */ #if 0 a = a + 1; /* nested comment */ b = b + 1; #endif 25 Rule #6 Fixed-width data types Rule #6: Whenever the width, in bits or bytes, of an integer value matters in the program, a fixedwidth data type shall be used in place of char, short, int, long, or long long. Use C99 s signed and unsigned fixed-width data types. Corollaries Keywords short and long shall never be used. Keyword char shall only be used for strings. 26 Copyright Barr Group, LLC. Page 13
RECOMMENDED FIXED-WIDTH TYPE NAMES Defined in C99 include file <stdint.h> Integer Width Signed Type Unsigned Type 8 bits / 1 byte int8_t uint8_t 16 bits / 2 bytes int16_t uint16_t 32 bits / 4 bytes int32_t uint32_t 64 bits / 8 bytes int64_t uint64_t Adopt these names even if not a C99 compiler! 27 Rule #7 bit-wise operators Rule: None of the bit-wise operators (&,, ~, <<, and >>) shall be used to manipulate signed types. Doesn t even really make sense Implementation defined Undefined behavior The C standard does not specify the underlying format of signed data (e.g., 2 s complement). Bit-wise operations rely on assumptions! 28 Copyright Barr Group, LLC. Page 14
NO BIT-WISE SIGNED EXAMPLE CODE DON T // Division via bit-shift doesn t work when negative. int8_t signed_data = -4; signed_data >>= 1; // not necessarily -2 // Left-shifts of signed data are undefined (in ISO C). int32_t signed_data = -100; signed_data <<= 1; // The meaning of bit flips also varies for signed data. uint8_t max_unsigned = ~0; // 255 (max unsigned) int8_t max_signed = ~0; // -1 (not max signed) 29 Rule #8 don t mix signed & unsigned Rule: Signed integers shall not be combined with unsigned integers in comparisons or expressions. Decimal constants meant to be unsigned should be declared with a u at the end. Several details of manipulation of binary data in signed integer containers are implementationdefined aspects of the ISO C language standard. 30 Results of mixing signed and unsigned data can lead to data-dependent bugs. Often encountered during integration Copyright Barr Group, LLC. Page 15
NO MIX-SIGNED EXAMPLE CODE DON T int s = -9; unsigned int u = 6; // WARNING! Dangerous mix of signed and unsigned. if (s + u < 4) { // This correct path should be executed // if (-9 + 6) is -3 < 4, as humans expect. } else { // This incorrect path is actually executed } 31 Rule #9 favor inline over macros Rule: Parameterized macros shall not be used if an inline function can be written to accomplish the same task. C++ compiler-hint keyword inline was added to C as part of C99. 32 Copyright Barr Group, LLC. Page 16
INLINE VS. MACROS EXAMPLE CODE DON T // No type checking; possible unintended side effects. #define SQUARE(A) ((A)*(A)) DO // Inline functions are safer, with no run-time cost. inline uint32_t square(uint16_t a) { return (a * a); } 33 Rule #10 one variable declaration per line Rule: Declare each variable on its own line The comma separator shall not be used within variable declarations. Generated code is identical Compilation isn t any slower either Biggest benefit: Improved readability DON T char * x, y; // Was y supposed to be a pointer too? 34 Copyright Barr Group, LLC. Page 17
KEY TAKEAWAYS Different sources of firmware bugs The original programmer creates some Maintenance programmers create others The original programmer has influence over both! Keep bugs out by adopting coding standard rules First, by enforcing bug-limiting rules Enforced automatically, whenever possible Improved portability, readability and maintainability 35 QUESTION & ANSWER 36 Copyright Barr Group, LLC. Page 18
ADDITIONAL RESOURCES Paper: Top 10 Bug-Killing Coding Standard Rules barrgroup.com/embedded-systems/how-to/bug-killing-standards-for-embedded-c Book: Embedded C Coding Standard barrgroup.com/coding-standard Kit: Embedded Software Training in a Box barrgroup.com/boot-camp-box Training: Barr Group s Upcoming Public Courses barrgroup.com/training-calendar 37 Copyright Barr Group, LLC. Page 19