1.33k likes | 1.34k Views
A close look at 15 problems one can find when reviewing C code.<br>Offers code examples.<br>Covers indexed loops, tainted data, copy and paste errors, problems with comparisons, exceptions, etc.<br>You can use static code analysis tools to make code review easier. Code analyzers find errors and potential vulnerabilities in code, while saving the developers' time and the companies' money.<br>Manual code review is expensive - a group of programmers get together regularly to review the code.<br>One can run static analysis tools regularly to find mistakes and vulnerabilities early.
E N D
A scrupulous code review – 15 bugs in C++ code PhillipKhandeliants khandeliants@viva64.com
Over 5 years with the PVS-Studio team Team lead at the C++ analyzer development team Microsoft Certified Professional, C# Talk on modern C++ Live by the C++ ISO standard
Intro: the code review • We all do code reviews • Who doesn't admit this – does it twice as often
void foo(const std::vector<....> &vec) • { • .... • for (auto i = 0; i < vec.size(); ++i) • { • // do some magic with vec[i] • .... • } • .... • }
void foo(const std::vector<....> &vec) • { • .... • for (inti = 0; i < vec.size(); ++i) // 64-bit problems :) • { • // do some magic with vec[i] • .... • } • .... • }
void foo(const std::vector<....> &vec) • { • .... • for (size_ti = 0; i < vec.size(); ++i) // ok • { • // do some magic with vec[i] • .... • } • .... • }
void foo(const std::vector<....> &vec) • { • .... • for (std::vector<....>::size_typei = 0; i < vec.size(); ++i) • { • // do some magic with vec[i] • .... • } • .... • }
void foo(const std::vector<....> &vec) • { • .... • for (auto i = 0uLL; i < vec.size(); ++i) // don't do that on • // 128-bit processors • { • // do some magic with vec[i] • .... • } • .... • }
void foo(const std::vector<....> &vec) • { • .... • for (auto i = 0uLLL; i < vec.size(); ++i) // ok since C++7d • { • // do some magic with vec[i] • .... • } • .... • }
void foo(const std::vector<....> &vec) • { • .... • for (auto i = 0uLLL; i < vec.size(); ++i) // ok since C++7d • { • // do some magic with vec[i] • .... • } • .... • }
template <typename T> • int ColumnDecimal<T>::compareAt(size_t n, size_t m, • const IColumn & rhs_, int) const • { • auto other = static_cast<const Self &>(rhs_); • const T &a = data[n]; • const T &b = other.data[m]; • return decimalLess<T>(b, a, other.scale, scale) • ? 1 • : (decimalLess<T>(a, b, scale, other.scale) ? -1 : 0); • }
template <typename T> • int ColumnDecimal<T>::compareAt(size_t n, size_t m, • const IColumn & rhs_, int) const • { • auto other = static_cast<const Self &>(rhs_); • const T &a = data[n]; • const T &b = other.data[m]; • return decimalLess<T>(b, a, other.scale, scale) • ? 1 • : (decimalLess<T>(a, b, scale, other.scale) ? -1 : 0); • }
template <typename T> • int ColumnDecimal<T>::compareAt(size_t n, size_t m, • const IColumn & rhs_, int) const • { • auto &other = static_cast<const Self &>(rhs_); • const T &a = data[n]; • const T &b = other.data[m]; • return decimalLess<T>(b, a, other.scale, scale) • ? 1 • : (decimalLess<T>(a, b, scale, other.scale) ? -1 : 0); • }
template <typename T> • int ColumnDecimal<T>::compareAt(size_t n, size_t m, • const IColumn & rhs_, int) const • { • decltype(auto) other = static_cast<const Self &>(rhs_); • const T &a = data[n]; • const T &b = other.data[m]; • return decimalLess<T>(b, a, other.scale, scale) • ? 1 • : (decimalLess<T>(a, b, scale, other.scale) ? -1 : 0); • }
void vector32_inc(std::vector<uint32_t> &v) • { • for (size_ti = 0; i < v.size(); i++) • { • v[i]++; • } • }
void vector32_inc(std::vector<uint32_t> &v) • { • for (size_ti = 0; i < v.size(); i++) • { • v[i]++; • } • } • void vector8_inc(std::vector<uint8_t> &v) • { • for (size_ti = 0; i < v.size(); i++) • { • v[i]++; • } • }
vector8_inc(std::vector<uint8_t> &): • mov rdx, QWORD PTR [rdi] ; it = begin() • cmprdx, QWORD PTR [rdi+8] ; if (it == end()) • je .L1 ; return • xoreax, eax; i = 0 • .L3: ; do { • add BYTE PTR [rdx+rax], 1 ; ++(*(it + i)) • mov rdx, QWORD PTR [rdi] ; it = begin() • add rax, 1 ; ++i • mov rcx, QWORD PTR [rdi+8] ; end = end() • sub rcx, rdx; end = end - it • cmprax, rcx • jb .L3 ; } while (i < end) • .L1: • ret vector32_inc(std::vector<uint32_t> &): mov rax, QWORD PTR [rdi] ; it = begin() mov rdx, QWORD PTR [rdi+8] ; end = end() sub rdx, rax; end = end - it mov rcx, rdx; size in bytes shrrcx, 2 ; size in elements je .L1 ; if (size == 0) ; return add rdx, rax; end = end + it .L3: ; do { add DWORD PTR [rax], 1 ; ++(*it) add rax, 4 ; ++it cmprax, rdx jne .L3 ; } while (it != end) .L1: ret
vector8_inc(std::vector<uint8_t> &): • mov rdx, QWORD PTR [rdi] ; it = begin() • cmprdx, QWORD PTR [rdi+8] ; if (it == end()) • je .L1 ; return • xoreax, eax; i = 0 • .L3: ; do { • add BYTE PTR [rdx+rax], 1 ; ++(*(it + i)) • mov rdx, QWORD PTR [rdi] ; it = begin() • add rax, 1 ; ++i • mov rcx, QWORD PTR [rdi+8] ; end = end() • sub rcx, rdx; end = end - it • cmprax, rcx • jb .L3 ; } while (i < end) • .L1: • ret vector32_inc(std::vector<uint32_t> &): mov rax, QWORD PTR [rdi] ; it = begin() mov rdx, QWORD PTR [rdi+8] ; end = end() sub rdx, rax; end = end - it mov rcx, rdx; size in bytes shrrcx, 2 ; size in elements je .L1 ; if (size == 0) ; return add rdx, rax; end = end + it .L3: ; do { add DWORD PTR [rax], 1 ; ++(*it) add rax, 4 ; ++it cmprax, rdx jne .L3 ; } while (it != end) .L1: ret
void vector8_inc(std::vector<uint8_t> &v) • { • for (size_ti = 0; i < v.size(); i++) • { • v[i]++; • } • }
void vector8_inc(std::vector<uint8_t> &v) • { • for (size_ti = 0; i < v.size(); i++) • { • v[i]++; • } • } • void vector8_inc(std::vector<uint8_t> &v) • { • auto it = v.begin(); • const auto end = v.end(); • for (; it != end; ++it) • { • ++(*it); • } • }
vector8_inc(std::vector<uint8_t> &): • mov rax, QWORD PTR [rdi] ; it = begin() • mov rdx, QWORD PTR [rdi+8] ; end = end() • cmprax, rdx; if (it == end) • je .L1 ; return • .L3: ; do { • add BYTE PTR [rax], 1; ++(*it) • add rax, 1 ; ++it • cmprax, rdx; }while (it != end) • .L1: • ret
vector8_inc(std::vector<uint8_t> &): • mov rax, QWORD PTR [rdi] ; it = begin() • mov rdx, QWORD PTR [rdi+8] ; end = end() • cmprax, rdx; if (it == end) • je .L1 ; return • .L3: ; do { • add BYTE PTR [rax], 1; ++(*it) • add rax, 1 ; ++it • cmprax, rdx; }while (it != end) • .L1: • ret
void vector8_inc(std::vector<uint8_t> &v) • { • auto it = v.begin(); • const auto end = v.end(); • for (; it != end; ++it) • { • ++(*it); • } • }
void vector8_inc(std::vector<uint8_t> &v) • { • auto it = v.begin(); • const auto end = v.end(); • for (; it != end; ++it) • { • ++(*it); • } • } • void vector8_inc(std::vector<uint8_t> &v) • { • for (auto &elem : v) • { • ++elem; • } • }
#include <cstring> • #include <memory> • void InputPassword(char *pswd); • void ProcessPassword(const char *pswd); • #define MAX_PASSWORD_LEN .... • void Foo() • { • char password[MAX_PASSWORD_LEN]; • InputPassword(password); • ProcessPassword(password); • memset(password, 0, sizeof(password)); • }
#include <cstring> • #include <memory> • void InputPassword(char *pswd); • void ProcessPassword(const char *pswd); • #define MAX_PASSWORD_LEN .... • void Foo() • { • char password[MAX_PASSWORD_LEN]; • InputPassword(password); • ProcessPassword(password); • memset(password, 0, sizeof(password)); • }
#include <cstring> • #include <memory> • void InputPassword(char *pswd); • void ProcessPassword(const char *pswd); • #define MAX_PASSWORD_LEN .... • void Foo() • { • char *password = new char[MAX_PASSWORD_LEN]; • InputPassword(password); • ProcessPassword(password); • memset(password, 0, MAX_PASSWORD_LEN); • delete[] password; • }
voidtds_answer_challenge(....) • { • #define MAX_PW_SZ 14 • .... • if (ntlm_v == 1) { • .... • /* with security is best be pedantic */ • memset(hash, 0, sizeof(hash)); • memset(passwd_buf, 0, sizeof(passwd_buf)); • memset(ntlm2_challenge, 0, sizeof(ntlm2_challenge)); • } else { • .... • } • }
typedef struct tds_answer • { • unsigned char lm_resp[24]; • unsigned char nt_resp[24]; • } TDSANSWER; • static TDSRETtds7_send_auth(....) • { • size_tcurrent_pos; • TDSANSWER answer; • .... • /* for security reason clear structure */ • memset(&answer, 0, sizeof(TDSANSWER)); • return tds_flush_packet(tds); • }
char* crypt_md5(const char* pw, const char* salt) • { • unsigned char final[MD5_SIZE]; • .... • /* Don't leave anything around in vm they could use. */ • memset(final, 0, sizeof final); • return passwd; • }
void MD4Engine::transform (UInt32 state[4], • const unsigned char block[64]) • { • UInt32 a = state[0], b = state[1], • c = state[2], d = state[3], x[16]; • decode(x, block, 64); • .... • /* Zeroize sensitive information. */ • std::memset(x, 0, sizeof(x)); • }
char* px_crypt_md5(const char *pw, const char *salt, • char *passwd, unsigned dstlen) • { • .... • unsigned char final[MD5_SIZE]; • .... • /* Don't leave anything around in vm they could use. */ • memset(final, 0, sizeof final); • .... • }
Ways to fix • Custom safe_memset + disabled LTO/WPO • Access a non-volatile object through a volatile pointer • Call memset through a volatile function pointer • Volatile assembly code • Memset + memory barrier • Disable compiler optimizations (-fno-builtin-memset) • C11: memset_s • Windows: RtlSecureZeroMemory • FreeBSD & OpenBSD: explicit_bzero • Linux Kernel: memzero_explicit
static const char* basic_gets(int *cnt) • { • .... • int c = getchar(); • if (c < 0) • { • if ( fgets(command_buf, sizeof(command_buf) - 1, stdin) • != command_buf) • { • break; • } • /* remove endline */ • command_buf[strlen(command_buf)-1] = '\0'; • break; • } • .... • }
static const char* basic_gets(int *cnt) • { • .... • int c = getchar(); • if (c < 0) • { • if ( fgets(command_buf, sizeof(command_buf) - 1, stdin) • != command_buf) • { • break; • } • /* remove endline */ • command_buf[strlen(command_buf)-1] = '\0'; • break; • } • .... • }
int main (int argc, char *argv[]) • { • .... • else if (fgets(readbuf, BUFSIZ, stdin) == NULL) • { • if (feof (stdin)) • break; • error (EXIT_FAILURE, errno, _("input error")); • } • if (readbuf[strlen(readbuf) - 1] == '\n') • readbuf[strlen(readbuf) - 1] = '\0'; • .... • }
int main (int argc, char *argv[]) • { • .... • else if (fgets(readbuf, BUFSIZ, stdin) == NULL) • { • if (feof (stdin)) • break; • error (EXIT_FAILURE, errno, _("input error")); • } • if (readbuf[strlen(readbuf) - 1] == '\n') • readbuf[strlen(readbuf) - 1] = '\0'; • .... • } CVE-2015-8948
int main (int argc, char *argv[]) • { • .... • else if (getline(&line, &linelen, stdin) == -1) • { • if (feof (stdin)) • break; • error (EXIT_FAILURE, errno, _("input error")); • } • if (line[strlen(line) - 1] == '\n') • line[strlen(line) - 1] = '\0'; • .... • }
int main (int argc, char *argv[]) • { • .... • else if (getline(&line, &linelen, stdin) == -1) • { • if (feof (stdin)) • break; • error (EXIT_FAILURE, errno, _("input error")); • } • if (line[strlen(line) - 1] == '\n') • line[strlen(line) - 1] = '\0'; • .... • } CVE-2016-6262