<<
Home , C99

Security-aware coding in and C++

● Generic notes

● C language related issues

● Operating system related issues

● Programming patterns

● C++ language related issues

Zoltán Porkoláb Security Aware C/C++ Coding 1 C language related issues

● Null terminated strings

● Buffer overflow

● Insecure i/o

● Tainted scalars

● Uninitialized values

● Race conditions

● Function parameters

● Syntactical considerations

Zoltán Porkoláb Security Aware C/C++ Coding 2 Buffer/Stack overflow

Zoltán Porkoláb Security Aware C/C++ Coding 3 Buffer/Stack overflow

Zoltán Porkoláb Security Aware C/C++ Coding 4 Null terminated strings char *strcpy(char *tgt, const char *src);

● Issues?

Zoltán Porkoláb Security Aware C/C++ Coding 5 Null terminated strings char *strcpy(char *tgt, const char *src);

● Undefined behavior if – the target array is not large enough – the strings overlap – target is not pointer to char array – the source is not null terminated – any of tgt or src is null pointer

Zoltán Porkoláb Security Aware C/C++ Coding 6 Null terminated strings char *strcpy(char *tgt, const char *src); char *strncpy(char *tgt, const char *src, size_t n);

● Undefined behavior if the strings overlap

● Copies at most n characters (chars after '\0' not copied)

● Fill the rest of the tgt buffer with null characters

● If there is no null character in the first n character of src, then the array tgt will not be null terminated

Zoltán Porkoláb Security Aware C/C++ Coding 7 Null terminated strings char *strcpy(char *tgt, const char *src); char *strncpy(char *tgt, const char *src, size_t n); strncpy( tgt, src, (tgt)); tgt[sizeof(tgt)-1] = '\0';

● Strncpy, strncat better to avoid

● Strncmp, … ok

Zoltán Porkoláb Security Aware C/C++ Coding 8 Null terminated strings char *strcpy(char *tgt, const char *src); size_t strlcpy(char *tgt, const char *src, size_t tsize); size_t strlcat(char *tgt, const char *src, size_t tsize);

● BSD defines strlcpy, strlcat – Truncates the source string (security risk) – Do not perform all run-time checks – Return value is not explicit about error

● The total length of the string attempted to create

Zoltán Porkoláb Security Aware C/C++ Coding 9 Null terminated strings char *strcpy(char *tgt, const char *src); size_t strlcpy(char *tgt, const char *src, size_t tsize); size_t strlcat(char *tgt, const char *src, size_t tsize);

● BSD defines strlcpy, strlcat – Truncates the source string (security risk) – Do not perform all run-time checks – Return value is not explicit about error

● The total length of the string attempted to create

Unix man page: Note, however, that if strlcat() traverses size characters without finding a NUL, the length of the string is considered to be size and the destination string will not be NUL-terminated (since there was no space for the NUL).

Zoltán Porkoláb Security Aware C/C++ Coding 10 Null terminated strings size_t strlcpy(char *tgt, const char *src, size_t tsize); size_t strlcat(char *tgt, const char *src, size_t tsize); char pname[MAXPATHLEN]; char *dir = ...; char *file = ...; if ( strlcpy( pname, dir, sizeof(pname)) >= sizeof(pname) ) { fprintf( stderr, “truncated!”); return -1; } if ( strlcat( pname, file, sizeof(pname)) >= sizeof(pname) ) { fprintf( stderr, “truncated!”); return -1; } /* ... */

Zoltán Porkoláb Security Aware C/C++ Coding 11 Do not do this! size_t strlcpy(char *tgt, const char *src, size_t tsize); size_t strlcat(char *tgt, const char *src, size_t tsize); char pname[MAXPATHLEN]; char *dir = ...; char *file = ...; size_t n = strlcpy( pname, dir, sizeof(pname)); if ( n >= sizeof(pname) ) { fprintf( stderr, “truncated!”); return -1; } if ( strlcpy( pname+n,file,sizeof(pname)-n) >= sizeof(pname)-n ) { fprintf( stderr, “truncated!”); return -1; } /* ... */

Zoltán Porkoláb Security Aware C/C++ Coding 12 Do not do this! size_t strlcpy(char *tgt, const char *src, size_t tsize); size_t strlcat(char *tgt, const char *src, size_t tsize); char pname[MAXPATHLEN]; char *dir = ...; char *file = ...; size_t n = strlcpy( pname, dir, sizeof(pname)); if ( n >= sizeof(pname) ) { fprintf( stderr, “truncated!”); return -1; } if ( strlcpy( pname+n,file,sizeof(pname)-n) >= sizeof(pname)-n ) { fprintf( stderr, “truncated!”); return -1; } However, one may question the /* ... */ validity of such optimizations, as they defeat the whole purpose of strlcpy() and strlcat(). As a matter of fact, the first version of this Zoltán Porkoláb manualSecurity Aware page C/C++ Codinggot it wrong. 13 Safe functions char *strcpy(char *tgt, const char *src); size_t strlcpy(char *tgt, const char *src, size_t tsize); errno_t strcpy_s(char *tgt, rsize_t tsize, const char *src);

● Calls the constraint handler function if – any of tgt or src is null pointer – tsize is zero or greater RSIZE_MAX – tsize <= strnlen_s(src, tsize) – tgt and src strings overlap

● Undefined if size of tgt array <= strlen_s(src,tsize) < tsize

Zoltán Porkoláb Security Aware C/C++ Coding 14 Safe functions

#define __STDC_WANT_LIB_EXT1__ 1 #include constraint_handler_t set_constraint_handler_s( constraint_handler_t handler )

● Safe functions are guaranteed to available if – __STDC_LIB_EXT1__ is defined by the implementation – User defines __STDC_WANT_LIB_EXT1__ before including

● Defined in : void (*constraint_handler_t) ( const char *msg, void *ptr, errno_t error);

● If constraint_handler is not set, default is implementation defined

Zoltán Porkoláb Security Aware C/C++ Coding 15 Safe functions strcpy_s( char *dest, rsize_t destsz, const char *src) strncpy_s(char *dest, rsize_t destsz, const char *src, rsize_t n) strcat_s( char *dest, rsize_t destsz, const char *src) strncat_s(char *dest, rsize_t destsz, const char *src, rsize_t n) strnlen_s( char *str, rsize_t strsz) strtok_s(char *str, rsize_t *strmax, const char *delim, char **p) memset_s( void *dest, rsize_t destsz, int ch, rsize_t n) memcpy_s( void *dest, rsize_t destsz, const void *src, rsize_t n) memmove_s(void *dest, rsize_t destsz, const void *src, rsize_t n) strerrorlen_s( errno_t errnum) strerror_s( char *buf, rsize_t bufsz, errno_t errnum)

Zoltán Porkoláb Security Aware C/C++ Coding 16 Safe functions strtok_s(char *str, rsize_t *strmax, const char *delim, char **p) char str[] = "A bird came down the walk"; rsize_t strmax = sizeof(str); const char *delim = " "; char *next_token; printf("Parsing the input string '%s'\n", str); char *token = strtok_s(str, &strmax, delim, &next_token); while(token) { puts(token); token = strtok_s(NULL, &strmax, delim, &next_token); }

Zoltán Porkoláb Security Aware C/C++ Coding 17 Safe functions strerrorlen_s( errno_t errnum); strerror_s( char *buf, rsize_t bufsz, errno_t errnum); setlocale(LC_ALL, "ja_JP.utf8"); size_t errmsglen = strerrorlen_s(errno) + 1; char errmsg[errmsglen]; strerror_s(errmsg, errmsglen, errno); fprintf(stderr, “Might be truncated: %s”, errmsg);

Zoltán Porkoláb Security Aware C/C++ Coding 18 Safe functions strerrorlen_s( errno_t errnum); strerror_s( char *buf, rsize_t bufsz, errno_t errnum); setlocale(LC_ALL, "ja_JP.utf8"); size_t errmsglen = strerrorlen_s(errno) + 1; char errmsg[errmsglen]; strerror_s(errmsg, errmsglen, errno); fprintf(stderr, “Might be truncated: %s”, errmsg);

strerror_s() is the only safe function that allows truncation

Zoltán Porkoláb Security Aware C/C++ Coding 19 Buffer overflow

#include #define BUFSIZE 80 char *answer(const char *question); int main() { printf(“answer is %s\n”, answer(“How are you?”)); return 0; } char *answer(const char *question) { char buffer[BUFSIZE]; printf(“%s\n”, question); gets(buffer); return buffer; }

Zoltán Porkoláb Security Aware C/C++ Coding 20 Buffer overflow

#include #define BUFSIZE 80 char *answer(const char *question); int main() { printf(“answer is %s\n”, answer(“How are you?”)); return 0; } char *answer(const char question) { char buffer[BUFSIZE]; printf(“%s\n”, question); gets(buffer); /* Possible buffer overflow! */ return buffer; }

Zoltán Porkoláb Security Aware C/C++ Coding 21 Buffer overflow

#include #define BUFSIZE 80 char *answer(const char *question); int main() { printf(“answer is %s\n”, answer(“How are you?”)); return 0; } char *answer(const char question) { char buffer[BUFSIZE]; printf(“%s\n”, question); fgets(buffer, BUFSIZE, stdin); /* Safe! */ return buffer; }

Zoltán Porkoláb Security Aware C/C++ Coding 22 Buffer overflow

#include #define BUFSIZE 80 char *answer(const char *question); int main() { printf(“answer is %s\n”, answer(“How are you?”)); return 0; } char *answer(const char question) { char buffer[BUFSIZE]; printf(“%s\n”, question); fgets(buffer, BUFSIZE, stdin); /* Safe! / return buffer; /* Life of buffer is about to end */ }

Zoltán Porkoláb Security Aware C/C++ Coding 23 Buffer overflow

#include #define BUFSIZE 80 char *answer(const char *question); int main() { printf(“answer is %s\n”, answer(“How are you?”)); return 0; } char *answer(const char question) { static char buffer[BUFSIZE]; /* Better, but not perfect */ printf(“%s\n”, question); fgets(buffer, BUFSIZE, stdin); /* Safe! */ return buffer; }

Zoltán Porkoláb Security Aware C/C++ Coding 24 Buffer overflow

#include #define BUFSIZE 80 char *answer(const char *question); int main() { char buffer[BUFSIZE]; /* Who owns buffer? */ printf(“answer is %s\n”, answer(“How are you?”, buffer, BUFSIZE)); return 0; } char *answer(const char *question, char *buf, int len) { printf(“%s\n”, question); fgets(buf, len, stdin); /* Safe! */ return buf; }

Zoltán Porkoláb Security Aware C/C++ Coding 25 C++ must be better

#include #define BUFSIZE 80 char *answer(const char *question); int main() { char buffer[BUFSIZE]; std::cout << “answer is “ << answer(“How are you?”, buffer, BUFSIZE)) << std::endl; return 0; } char *answer(const char *question, char *buf, int len) { std::cout << question std::cin >> buffer; return buf; }

Zoltán Porkoláb Security Aware C/C++ Coding 26 C++ must be better?

#include #define BUFSIZE 80 char *answer(const char *question); int main() { char buffer[BUFSIZE]; std::cout << “answer is “ << answer(“How are you?”, buffer, BUFSIZE)) << std::endl; return 0; } char *answer(const char *question, char *buf, int len) { std::cout << question; std::cin.getline(buffer,len); // Safer return buf; }

Zoltán Porkoláb Security Aware C/C++ Coding 27 C++ is better!

#include #include std::string answer(std::string question); int main() { std::cout << “answer is “ << answer(“How are you?”) << std::endl; return 0; } std::string answer(std::string question) { std::cout << question; std::string buffer; std::cin >> buffer; // Safe return buffer; } Zoltán Porkoláb Security Aware C/C++ Coding 28 Not only input can be dangerous

#include #include int main() { char buffer[10]; long long l; scanf( “%lld”, &l); sprintf( buffer, “%lld”, l); }

Zoltán Porkoláb Security Aware C/C++ Coding 29 Tainted format string

#include #include int incorrect_passwd(const char *user) { int ret; static const char msgfmt[] = “%s cannot be authenticated\n”; size_t len = strlen(user) + sizeof(msgfmt); char *msg = (char *)malloc(len); if ( NULL == msg ) return -1; ret = snprintf(msg,len,msgfmt,user); if ( ret >= len ) { free(msg); return -1; } fprintf(stderr, msg); free(msg); }

Zoltán Porkoláb Security Aware C/C++ Coding 30 Tainted format string

#include #include int incorrect_passwd(const char *user) { int ret; static const char msgfmt[] = “%s cannot be authenticated\n”; size_t len = strlen(user) + sizeof(msgfmt); char *msg = (char *)malloc(len); if ( NULL == msg ) return -1; ret = snprintf(msg,len,msgfmt,user); if ( ret >= len ) { free(msg); return -1; } fprintf(stderr, msg); free(msg); }

Zoltán Porkoláb Security Aware C/C++ Coding 31 Not only input can be dangerous

#include #include int main() { char buffer[10]; fgets(buffer,stdin); printf( buffer); }

Zoltán Porkoláb Security Aware C/C++ Coding 32 Safe i/o functions fopen_s(FILE **streamptr, const char *fname, const char *mode) freopen_s(FILE **streamptr, const char *fname, const char *mode,FILE *oldstream) gets_s( char *str, rsize_t n) scanf_s( const char *format, ...) fscanf_s(FILE *fp, const char *format, ...) sscanf_s(char *buffer, const char *format, ...) snprintf(char *buffer, size_t bufsz, const char *format ...) printf_s(const char *format ...) fprintf_s(FILE *fp, const char *format ...) sprintf_s(char *buffer, size_t bufsz, const char *format ...) snprintf_s(char *buffer, size_t bufsz, const char *format ...)

Zoltán Porkoláb Security Aware C/C++ Coding 33 Stack overflow

● Sentinels

● Address space randomization

● Stack randomization by OS

● Stack randomization by software

Similar issues with the heap

Zoltán Porkoláb Security Aware C/C++ Coding 34 String comparison

void f() { std::string s(“Hello”); if( “Hello” == s.c_str() ) { /* … */ } }

Zoltán Porkoláb Security Aware C/C++ Coding 35 String comparison

void f() { std::string s(“Hello”); if( “Hello” == s.c_str() ) { /* … */ } }

● Comparing two pointers… … likely never true

Zoltán Porkoláb Security Aware C/C++ Coding 36 strcmp

void f() { std::string s(“Hello”); if( std::strcmp(“Hello”, s.c_str()) == 0) { /* … */ } }

Zoltán Porkoláb Security Aware C/C++ Coding 37 strcmp

void f() { std::string s(“Hello”); if( std::strcmp(“Hallo”, s.c_str()) == -1 ) { /* … */ } }

Zoltán Porkoláb Security Aware C/C++ Coding 38 strcmp

void f() { std::string s(“Hello”); if( std::strcmp(“Hallo”, s.c_str()) == -1 ) { /* … */ } }

Zoltán Porkoláb Security Aware C/C++ Coding 39 strcmp

void f() { std::string s(“Hello”); if( std::strcmp(“Hallo”, s.c_str()) < 0 ) { /* … */ } }

Zoltán Porkoláb Security Aware C/C++ Coding 40 strcmp

void f() { std::string s(“Hello”); if( std::strcmp(“Hallo”, s.c_str()) < 0 ) { /* … */ } } int strcmp( const char *s1, const char *s2) { while ( *s1 == *s2 && *s1 != '\0' ) { ++s1; ++s2; } return *s1 - *s2; }

Zoltán Porkoláb Security Aware C/C++ Coding 41 Cheating strings char gpl[] = "This is a GPL license"; char fake[] = "This is a GPL license or maybe not"; std::string str1(fake); std::cerr << fake << std::endl; std::cerr << str1 << std::endl; std::cerr << str1.c_str() << std::endl; std::cerr << (str1 == gpl) << std::endl; std::cerr << (0 == strcmp(str1.c_str(),gpl)) << std::endl; This is a GPL license or maybe not This is a GPL license or maybe not This is a GPL license or maybe not 0 0

Zoltán Porkoláb Security Aware C/C++ Coding 42 Cheating strings char gpl[] = "This is a GPL license"; char fake[] = "This is a GPL license or maybe not"; str1[21] = '\0'; std::string str1(fake); std::cerr << fake << std::endl; std::cerr << str1 << std::endl; std::cerr << str1.c_str() << std::endl; std::cerr << (str1 == gpl) << std::endl; std::cerr << (0 == strcmp(str1.c_str(),gpl)) << std::endl; This is a GPL license This is a GPL licenseor maybe not This is a GPL license 0 1

Zoltán Porkoláb Security Aware C/C++ Coding 43 Cheating strings

Linuxant's HSM Modem drivers MODULE_LICENSE(“GPL\0 ...”) https://linux.slashdot.org/story/04/04/27/1435217/kernel- modules-that-lie-about-their-licenses tainted kernel issue

Mozilla/2.02 [fr] (WinNT; I) Mozilla/4.0 (compatible; MSIE 4.0; Windows 95)

Zoltán Porkoláb Security Aware C/C++ Coding 44 Safe conversions char buffer[BUFSIZE]; //... int i = atoi( buffer); long strtol( const char *str, char **str_end, int base);

// since C99 str and str_end are pointers long i1 = strtol(p, &end, 10); long i2 = strtol(p, NULL, 10); // no error handling long i3 = strtol(p, &end, 0); // auto-detect base long i = strtol(p, &end, 10); if ( ERANGE == errno ) // range error

Zoltán Porkoláb Security Aware C/C++ Coding 45 C++ std::string

void f() { std::string s(“Hello”); if( std::strcmp(“Hello”, s.data()) == 0) { /* … */ } }

Zoltán Porkoláb Security Aware C/C++ Coding 46 C++ std::string

void f() { std::string s(“Hello”); if( std::strcmp(“Hello”, s.data()) == 0) { /* … */ } }

● The returned array is not required to be null-terminated. until C++11

● The returned array is null-terminated, that is, data() and c_str() perform the same function. since C++11

Zoltán Porkoláb Security Aware C/C++ Coding 47 C++ std::string

void f() { std::string s(“Hello”); if( std::strcmp(“Hello”, s.data()) == 0) { /* … */ } }

● The returned array is not required to be null-terminated. until C++11

● The returned array is null-terminated, that is, data() and c_str() perform the same function. since C++11

Zoltán Porkoláb Security Aware C/C++ Coding 48 C++ std::string

void f() { std::string s(“Hello”); if( std::strcmp(“Hello”, &s[0]) == 0) { /* … */ } }

Zoltán Porkoláb Security Aware C/C++ Coding 49 C++ std::string

void f() { std::string s(“Hello”); if( std::strcmp(“Hello”, &s[0]) == 0) { /* … */ } }

● The returned array is not required to be null-terminated. until C++11

● The returned array is null-terminated. since C++11

Zoltán Porkoláb Security Aware C/C++ Coding 50 C++ std::string

void f() { std::string s; if( std::strcmp(“Hello”, s.data()) == 0) { /* … */ } }

Zoltán Porkoláb Security Aware C/C++ Coding 51 C++ std::string

void f() { std::string s; if( std::strcmp(“Hello”, s.data()) == 0) { /* … */ } }

● If empty() returns true, the pointer is a non-null pointer that should not be dereferenced. until C++11

● If empty() returns true, the pointer points to a single null character. since C++11

Zoltán Porkoláb Security Aware C/C++ Coding 52 Practice

● Write a simple console program

● Read a secret passphrase from standard input

● Count, how many words in it

● Print the number of words to the standard output

● Be care, that the program does not store the passphrase, e.g. if the program crashes, one cannot dig it out from the core file

Zoltán Porkoláb Security Aware C/C++ Coding 53 Compiler optimization

#include void consume(char *); void main() { int i; char secret[BUFSIZE]; fgets( secret,v BUFSIZE, stdin); // use secret consume(secret);

}

Zoltán Porkoláb Security Aware C/C++ Coding 54 Compiler optimization

#include void consume(char *); void main() { int i; char secret[BUFSIZE]; fgets( secret,v BUFSIZE, stdin); // use secret consume(secret); // erase secret for security reasons memset(secret, 0, sizeof(secret)); // check secret for(i = 0; i < BUFSIZE; ++i) printf(“d”,secret[i]); }

Zoltán Porkoláb Security Aware C/C++ Coding 55 https://godbolt.org/

Zoltán Porkoláb Security Aware C/C++ Coding 56 Compiler optimization

#include void consume(char *); void main() { int i; char secret[BUFSIZE]; fgets( secret,v BUFSIZE, stdin); // use secret consume(secret); // erase secret for security reasons memset(secret, 0, sizeof(secret));

}

Zoltán Porkoláb Security Aware C/C++ Coding 57 Compiler optimization

Zoltán Porkoláb Security Aware C/C++ Coding 58 -O2

Zoltán Porkoláb Security Aware C/C++ Coding 59 Compiler optimization

#include void consume(char *); void* (*fp)(void*,int,size_t); void main() { int i; fp = memset; char secret[BUFSIZE]; fgets( secret,v BUFSIZE, stdin); // use secret consume(secret); // erase secret for security reasons fp(secret, 0, sizeof(secret)); }

Zoltán Porkoláb Security Aware C/C++ Coding 60 -O2

Zoltán Porkoláb Security Aware C/C++ Coding 61 Compiler optimization

#include void consume(char *); volatile void* (*fp)(void*,int,size_t); void main() { int i; fp = memset; char secret[BUFSIZE]; fgets( secret,v BUFSIZE, stdin); // use secret consume(secret); // erase secret for security reasons fp(secret, 0, sizeof(secret)); }

Zoltán Porkoláb Security Aware C/C++ Coding 62 -O2

Zoltán Porkoláb Security Aware C/C++ Coding 63 Compiler optimization

#include void consume(char *); int main() { int i; char secret[BUFSIZE]; fgets( secret,v BUFSIZE, stdin); // use secret consume(secret); // erase secret for security reasons memset_s(secret, sizeof(secret), 0, sizeof(secret)); }

Zoltán Porkoláb Security Aware C/C++ Coding 64 -O2

Zoltán Porkoláb Security Aware C/C++ Coding 65