Static analysis and writing C/C++ of high quality code for embedded systems Phillip Khandeliants, PVS-Studio
Speaker 2 • Phillip Khandeliants, 1994 • C++/C# developer in the PVS-Studio team • 3 years of taking part in developing the C++ analyzer core • Authour of articles on checking open source projects
Projected growth rate of IoT 3 15.41 17.68 20.35 23.14 26.66 30.73 35.82 42.62 51.11 62.12 75.44 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 Source: https://www.statista.com/statistics/471264/iot-number-of-connected-devices-worldwide/
And projects keep growing… • RT-Thread 1.2.0: 1 810 000 lines of code • RT-Thread 4.0.0 - 5.5 times larger: 9 903 000 lines of code • Linux kernel 1.0.0 : 177 000 lines of code • Linux kernel 5.1-rc2 - 118 times larger: 20 896 000 lines of code 4
What is an error capable of? 5
What is an error capable of? 6
Fascinating bug 7 • A colleague made 4 robots • They were controlled by a remote console • They were playing football and «catch the mice» • One of the robots went crazy due to a programming error
8
9 Source: https://www.nist.gov/sites/default/files/documents/director/planning/report02-3.pdf
Code review • Helps to find errors of high level and not to shoot off your legs waist-high • Allows to share experience with padawans • Together you’ll learn a lot of new secret things about the project 10
But... • Code review is to expensive: – Expectation: «It’ll take us 10-15 mins to review the edit» – Reality – we stay long for hours • You get tired too quickly from code viewing 11
Why code review doesn’t always work 12 static int EatWhitespace(FILE * InFile) { int c; for (c = getc(InFile); isspace(c) && ('n' != c); c = getc(InFile)); return (c); }
Why code review doesn’t always work 13 #ifdef isspace #undef isspace #endif .... #define isspace(c) ((c)==' ' || (c) == 't') .... for (c = getc(InFile); ((c)==' ' || (c) == 't') && ('n' != c); c = getc(InFile)); V560 A part of conditional expression is always true: ('n' != c). params.c 136.
Dynamic code analysis • Debuggers • Profilers • Sanitizers (AddressSanitizer, ThreadSanitizer, ...) • Found a bug? Let's run for fixing it! 14
But again... • It’s not always simple to test and debug embedded code • Sanitizers and profilers are slow • You’ll often need some special test dataset 15
Static analysis for the rescue • Automated code review by a machine • A machine doesn’t get tired  • You can find the most exciting error patterns  16
Where has the error crept? 17 static void SHA1Final(unsigned char digest[20], SHA1_CTX *context) { u32 i; unsigned char finalcount[8]; .... memset(context->count, 0, 8); memset(finalcount, 0, 8); }
Here it is! 18 CWE-14 V597 The compiler could delete the 'memset' function call, which is used to flush 'finalcount' buffer. The memset_s() function should be used to erase the private data. wifi_generate_pin.c 185 static void SHA1Final(unsigned char digest[20], SHA1_CTX *context) { u32 i; unsigned char finalcount[8]; .... memset(context->count, 0, 8); memset(finalcount, 0, 8); }
Static Application Security Testing (SAST) Programming error Real vulnerability 19 • «Programming errors are boring to catch!» • «You’d better catch vulnerabilities!» • NIST: 64% of vulnerabilities are programming errors • Let’s start «intimidating» managers and team leads with potential vulnerabilities Potential vulnerability
Two kinds of SAST • Search for known vulnerabilities in code • Preventive measures from potential vulnerabilities 20
• CWE™ is a community-developed list of common software security weaknesses. • Set of weaknesses/potential vulnerabilities, which can become real vulnerabilities(CVE). One just needs to describe how to exploit them . • Website: https://cwe.mitre.org • CWE List Version 3.1 contains 716 potential vulnerabilities. 21 Common Weakness Enumeration (CWE)
Examples of potential vulnerabilities according to CWE • CWE-20: Improper Input Validation • CWE-369: Divide By Zero • CWE-457: Use of Uninitialized Variable • CWE-467: Use of sizeof() on a Pointer Type • CWE-562: Return of Stack Variable Address 22
CWE-20: Improper Input Validation 23 if (c < 0) { if (fgets(command_buf, sizeof(command_buf) - 1, stdin) != command_buf) { break; } command_buf[strlen(command_buf) - 1] = '0'; /* remove endline */ break; }
CWE-20: Improper Input Validation 24 if (c < 0) { if (fgets(command_buf, sizeof(command_buf) - 1, stdin) != command_buf) { break; } command_buf[strlen(command_buf) - 1] = '0'; /* remove endline */ break; } '0' 0 -1 CWE-20 V1010 Unchecked tainted data is used in index: 'strlen(command_buf)'.
CWE-562: Return of Stack Variable Address 25 void SEMC_GetDefaultConfig(semc_config_t *config) { assert(config); semc_axi_queueweight_t queueWeight; /*!< AXI queue weight. */ semc_queuea_weight_t queueaWeight; semc_queueb_weight_t queuebWeight; .... config->queueWeight.queueaWeight = &queueaWeight; config->queueWeight.queuebWeight = &queuebWeight; }
CWE-562: Return of Stack Variable Address 26 void SEMC_GetDefaultConfig(semc_config_t *config) { assert(config); semc_axi_queueweight_t queueWeight; /*!< AXI queue weight. */ semc_queuea_weight_t queueaWeight; semc_queueb_weight_t queuebWeight; .... config->queueWeight.queueaWeight = &queueaWeight; config->queueWeight.queuebWeight = &queuebWeight; } CWE-562 V506 Pointer to local variable 'queuebWeight' is stored outside the scope of this variable. Such a pointer will become invalid. fsl_semc.c 257
Common Vulnerabilities and Exposures (CVE) • CVE — real vulnerabilities, there are confirmed scenarios of their exploitation • Main website: https://cve.mitre.org/ • Total CVE Entries: 114 282 27
CVE-2012-2122 28 typedef char my_bool; my_bool check_scramble(const char *scramble_arg, const char *message, const uint8 *hash_stage2) { .... return memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE); } V642 Saving the 'memcmp' function result inside the 'char' type variable is inappropriate. The significant bits could be lost breaking the program's logic. password.c
CVE-2012-2122 29 typedef char my_bool; my_bool check_scramble(const char *scramble_arg, const char *message, const uint8 *hash_stage2) { .... return memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE); } V642 Saving the 'memcmp' function result inside the 'char' type variable is inappropriate. The significant bits could be lost breaking the program's logic. password.c
CVE-2014-1266 30 static OSStatus SSLVerifySignedServerKeyExchange(....) { OSStatus err; .... if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0) goto fail; if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) goto fail; goto fail; if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0) goto fail; .... fail: ....; }
CVE-2014-1266 31 static OSStatus SSLVerifySignedServerKeyExchange(....) { OSStatus err; .... if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0) goto fail; if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) goto fail; goto fail; if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0) goto fail; .... fail: ....; } • V640 The code's operational logic does not correspond with its formatting. The statement is indented to the right, but it is always executed. It is possible that curly brackets are missing. • V779 Unreachable code detected. It is possible that an error is present
A path from an «ordinary» error to a vulnerability 32
SEI CERT coding standard • Developed by CERT (CERT Coordination Center, CERT/CC) • Meant for C, C++, Java, Perl languages • Is quite similar to CWE 33
Examples of rules from SEI CERT • MSC06-C: Beware of compiler optimizations • INT33-C: Ensure that division and remainder operations do not result in divide-by-zero errors • EXP33-C, EXP53-CPP: Do not read uninitialized memory • ARR01-C: Do not apply the sizeof operator to a pointer when taking the size of an array • DCL30-C: Declare objects with appropriate storage durations 34
EXP34-C. Do not dereference null pointers, C/C++ 35 EOLIAN void _evas_canvas_key_lock_add( ...., Evas_Public_Data *e, ....) { .... e->locks.lock.list = realloc(e->locks.lock.list, e->locks.lock.count * sizeof(char *)); e->locks.lock.list[e->locks.lock.count - 1] = strdup(keyname); eina_hash_free_buckets(e->locks.masks); }
EXP34-C. Do not dereference null pointers, C/C++ 36 EXP34-C V701 realloc() possible leak: when realloc() fails in allocating memory, original pointer 'e->locks.lock.list' is lost. Consider assigning realloc() to a temporary pointer. evas_key.c 142 EOLIAN void _evas_canvas_key_lock_add( ...., Evas_Public_Data *e, ....) { .... e->locks.lock.list = realloc(e->locks.lock.list, e->locks.lock.count * sizeof(char *)); e->locks.lock.list[e->locks.lock.count - 1] = strdup(keyname); eina_hash_free_buckets(e->locks.masks); }
MISRA С/С++ coding standards • You’ll have to pay for it • MISRA C 2012 contains 143 rules, MISRA C++ 2008 — 228 rules • MISRA reduces the likelihood that an error will get into the code base 37
Examples of MISRA-rules • Don’t use octal constants • Don’t use goto • A function has to have single exit point • Don’t use standard library functions (atof/…/abort/exit/getenv/system/…) • Don’t use dynamic allocations • Don’t use unions • Each case has to end with break or throw 38
Story about Toyota not following the MISRA standard 39 • NHTSA: in 2000-2010 89 people died and 57 – sustained injuries in accidents • NHTSA and NASA carried out investigations • 7134 violations have been detected • Toyota: «You’re all wrong!» • …. • Toyota pays off 16 billions dollars in the pre- trial order
How to use MISRA and static analysis incorrectly 40
How to do it right? 41 • In a perfect world you run it on a developer’s machine • The error doesn’t get into the version control system • The developer won’t be afraid of embarrassment • Additional defence line – night runs on a CI- server
But 100500 warnings are still here! 42 • You just need to supress all of them after the first run! • Only the newly written code is analysed • Gradually fix old warnings
Shall I use static analysis instead of other methodologies? • Static analysis is not a silver bullet • Static analysis is the answer to the question: "How to make our code better?" • What does mean " better "? It will be easier to maintain and develop it, eliminate problems in it 43
Q&A 44 Check your project using PVS-Studio for programming errors and potential vulnerabilities! C, C++, C#, Java www.viva64.com

Static analysis and writing C/C++ of high quality code for embedded systems

  • 1.
    Static analysis andwriting C/C++ of high quality code for embedded systems Phillip Khandeliants, PVS-Studio
  • 2.
    Speaker 2 • Phillip Khandeliants,1994 • C++/C# developer in the PVS-Studio team • 3 years of taking part in developing the C++ analyzer core • Authour of articles on checking open source projects
  • 3.
    Projected growth rateof IoT 3 15.41 17.68 20.35 23.14 26.66 30.73 35.82 42.62 51.11 62.12 75.44 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 Source: https://www.statista.com/statistics/471264/iot-number-of-connected-devices-worldwide/
  • 4.
    And projects keepgrowing… • RT-Thread 1.2.0: 1 810 000 lines of code • RT-Thread 4.0.0 - 5.5 times larger: 9 903 000 lines of code • Linux kernel 1.0.0 : 177 000 lines of code • Linux kernel 5.1-rc2 - 118 times larger: 20 896 000 lines of code 4
  • 5.
    What is anerror capable of? 5
  • 6.
    What is anerror capable of? 6
  • 7.
    Fascinating bug 7 • Acolleague made 4 robots • They were controlled by a remote console • They were playing football and «catch the mice» • One of the robots went crazy due to a programming error
  • 8.
  • 9.
  • 10.
    Code review • Helpsto find errors of high level and not to shoot off your legs waist-high • Allows to share experience with padawans • Together you’ll learn a lot of new secret things about the project 10
  • 11.
    But... • Code reviewis to expensive: – Expectation: «It’ll take us 10-15 mins to review the edit» – Reality – we stay long for hours • You get tired too quickly from code viewing 11
  • 12.
    Why code reviewdoesn’t always work 12 static int EatWhitespace(FILE * InFile) { int c; for (c = getc(InFile); isspace(c) && ('n' != c); c = getc(InFile)); return (c); }
  • 13.
    Why code reviewdoesn’t always work 13 #ifdef isspace #undef isspace #endif .... #define isspace(c) ((c)==' ' || (c) == 't') .... for (c = getc(InFile); ((c)==' ' || (c) == 't') && ('n' != c); c = getc(InFile)); V560 A part of conditional expression is always true: ('n' != c). params.c 136.
  • 14.
    Dynamic code analysis •Debuggers • Profilers • Sanitizers (AddressSanitizer, ThreadSanitizer, ...) • Found a bug? Let's run for fixing it! 14
  • 15.
    But again... • It’snot always simple to test and debug embedded code • Sanitizers and profilers are slow • You’ll often need some special test dataset 15
  • 16.
    Static analysis forthe rescue • Automated code review by a machine • A machine doesn’t get tired  • You can find the most exciting error patterns  16
  • 17.
    Where has theerror crept? 17 static void SHA1Final(unsigned char digest[20], SHA1_CTX *context) { u32 i; unsigned char finalcount[8]; .... memset(context->count, 0, 8); memset(finalcount, 0, 8); }
  • 18.
    Here it is! 18 CWE-14V597 The compiler could delete the 'memset' function call, which is used to flush 'finalcount' buffer. The memset_s() function should be used to erase the private data. wifi_generate_pin.c 185 static void SHA1Final(unsigned char digest[20], SHA1_CTX *context) { u32 i; unsigned char finalcount[8]; .... memset(context->count, 0, 8); memset(finalcount, 0, 8); }
  • 19.
    Static Application SecurityTesting (SAST) Programming error Real vulnerability 19 • «Programming errors are boring to catch!» • «You’d better catch vulnerabilities!» • NIST: 64% of vulnerabilities are programming errors • Let’s start «intimidating» managers and team leads with potential vulnerabilities Potential vulnerability
  • 20.
    Two kinds ofSAST • Search for known vulnerabilities in code • Preventive measures from potential vulnerabilities 20
  • 21.
    • CWE™ isa community-developed list of common software security weaknesses. • Set of weaknesses/potential vulnerabilities, which can become real vulnerabilities(CVE). One just needs to describe how to exploit them . • Website: https://cwe.mitre.org • CWE List Version 3.1 contains 716 potential vulnerabilities. 21 Common Weakness Enumeration (CWE)
  • 22.
    Examples of potentialvulnerabilities according to CWE • CWE-20: Improper Input Validation • CWE-369: Divide By Zero • CWE-457: Use of Uninitialized Variable • CWE-467: Use of sizeof() on a Pointer Type • CWE-562: Return of Stack Variable Address 22
  • 23.
    CWE-20: Improper InputValidation 23 if (c < 0) { if (fgets(command_buf, sizeof(command_buf) - 1, stdin) != command_buf) { break; } command_buf[strlen(command_buf) - 1] = '0'; /* remove endline */ break; }
  • 24.
    CWE-20: Improper InputValidation 24 if (c < 0) { if (fgets(command_buf, sizeof(command_buf) - 1, stdin) != command_buf) { break; } command_buf[strlen(command_buf) - 1] = '0'; /* remove endline */ break; } '0' 0 -1 CWE-20 V1010 Unchecked tainted data is used in index: 'strlen(command_buf)'.
  • 25.
    CWE-562: Return ofStack Variable Address 25 void SEMC_GetDefaultConfig(semc_config_t *config) { assert(config); semc_axi_queueweight_t queueWeight; /*!< AXI queue weight. */ semc_queuea_weight_t queueaWeight; semc_queueb_weight_t queuebWeight; .... config->queueWeight.queueaWeight = &queueaWeight; config->queueWeight.queuebWeight = &queuebWeight; }
  • 26.
    CWE-562: Return ofStack Variable Address 26 void SEMC_GetDefaultConfig(semc_config_t *config) { assert(config); semc_axi_queueweight_t queueWeight; /*!< AXI queue weight. */ semc_queuea_weight_t queueaWeight; semc_queueb_weight_t queuebWeight; .... config->queueWeight.queueaWeight = &queueaWeight; config->queueWeight.queuebWeight = &queuebWeight; } CWE-562 V506 Pointer to local variable 'queuebWeight' is stored outside the scope of this variable. Such a pointer will become invalid. fsl_semc.c 257
  • 27.
    Common Vulnerabilities andExposures (CVE) • CVE — real vulnerabilities, there are confirmed scenarios of their exploitation • Main website: https://cve.mitre.org/ • Total CVE Entries: 114 282 27
  • 28.
    CVE-2012-2122 28 typedef char my_bool; my_boolcheck_scramble(const char *scramble_arg, const char *message, const uint8 *hash_stage2) { .... return memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE); } V642 Saving the 'memcmp' function result inside the 'char' type variable is inappropriate. The significant bits could be lost breaking the program's logic. password.c
  • 29.
    CVE-2012-2122 29 typedef char my_bool; my_boolcheck_scramble(const char *scramble_arg, const char *message, const uint8 *hash_stage2) { .... return memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE); } V642 Saving the 'memcmp' function result inside the 'char' type variable is inappropriate. The significant bits could be lost breaking the program's logic. password.c
  • 30.
    CVE-2014-1266 30 static OSStatus SSLVerifySignedServerKeyExchange(....) { OSStatuserr; .... if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0) goto fail; if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) goto fail; goto fail; if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0) goto fail; .... fail: ....; }
  • 31.
    CVE-2014-1266 31 static OSStatus SSLVerifySignedServerKeyExchange(....) { OSStatuserr; .... if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0) goto fail; if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) goto fail; goto fail; if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0) goto fail; .... fail: ....; } • V640 The code's operational logic does not correspond with its formatting. The statement is indented to the right, but it is always executed. It is possible that curly brackets are missing. • V779 Unreachable code detected. It is possible that an error is present
  • 32.
    A path froman «ordinary» error to a vulnerability 32
  • 33.
    SEI CERT codingstandard • Developed by CERT (CERT Coordination Center, CERT/CC) • Meant for C, C++, Java, Perl languages • Is quite similar to CWE 33
  • 34.
    Examples of rulesfrom SEI CERT • MSC06-C: Beware of compiler optimizations • INT33-C: Ensure that division and remainder operations do not result in divide-by-zero errors • EXP33-C, EXP53-CPP: Do not read uninitialized memory • ARR01-C: Do not apply the sizeof operator to a pointer when taking the size of an array • DCL30-C: Declare objects with appropriate storage durations 34
  • 35.
    EXP34-C. Do notdereference null pointers, C/C++ 35 EOLIAN void _evas_canvas_key_lock_add( ...., Evas_Public_Data *e, ....) { .... e->locks.lock.list = realloc(e->locks.lock.list, e->locks.lock.count * sizeof(char *)); e->locks.lock.list[e->locks.lock.count - 1] = strdup(keyname); eina_hash_free_buckets(e->locks.masks); }
  • 36.
    EXP34-C. Do notdereference null pointers, C/C++ 36 EXP34-C V701 realloc() possible leak: when realloc() fails in allocating memory, original pointer 'e->locks.lock.list' is lost. Consider assigning realloc() to a temporary pointer. evas_key.c 142 EOLIAN void _evas_canvas_key_lock_add( ...., Evas_Public_Data *e, ....) { .... e->locks.lock.list = realloc(e->locks.lock.list, e->locks.lock.count * sizeof(char *)); e->locks.lock.list[e->locks.lock.count - 1] = strdup(keyname); eina_hash_free_buckets(e->locks.masks); }
  • 37.
    MISRA С/С++ codingstandards • You’ll have to pay for it • MISRA C 2012 contains 143 rules, MISRA C++ 2008 — 228 rules • MISRA reduces the likelihood that an error will get into the code base 37
  • 38.
    Examples of MISRA-rules •Don’t use octal constants • Don’t use goto • A function has to have single exit point • Don’t use standard library functions (atof/…/abort/exit/getenv/system/…) • Don’t use dynamic allocations • Don’t use unions • Each case has to end with break or throw 38
  • 39.
    Story about Toyotanot following the MISRA standard 39 • NHTSA: in 2000-2010 89 people died and 57 – sustained injuries in accidents • NHTSA and NASA carried out investigations • 7134 violations have been detected • Toyota: «You’re all wrong!» • …. • Toyota pays off 16 billions dollars in the pre- trial order
  • 40.
    How to useMISRA and static analysis incorrectly 40
  • 41.
    How to doit right? 41 • In a perfect world you run it on a developer’s machine • The error doesn’t get into the version control system • The developer won’t be afraid of embarrassment • Additional defence line – night runs on a CI- server
  • 42.
    But 100500 warningsare still here! 42 • You just need to supress all of them after the first run! • Only the newly written code is analysed • Gradually fix old warnings
  • 43.
    Shall I usestatic analysis instead of other methodologies? • Static analysis is not a silver bullet • Static analysis is the answer to the question: "How to make our code better?" • What does mean " better "? It will be easier to maintain and develop it, eliminate problems in it 43
  • 44.
    Q&A 44 Check your projectusing PVS-Studio for programming errors and potential vulnerabilities! C, C++, C#, Java www.viva64.com