Thursday, 29 November 2018

Cppcheck: Basic Checks

In my previous posts, I've given an intro to Cppcheck and also described how to integrate it with CMake.

In this post I'm going to start looking at some of the errors that Cppcheck can help you find in your code.

Memory Leaks

Every C++ developer will have at some point written code which includes a memory leak. With modern C++ we have tools such as std::unique_ptr and std::shared_ptr to help with resource allocation, however, when using raw pointers (e.g. in legacy code), using a tool such as Cppcheck can help catch errors. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <string>
#include <iostream>
 
std::string* my_to_string(int x) {
    return new std::string(to_string(x));
}
 
void print_ints() {
    for(int i = 0; i < 10; ++i) {
        std::string* tmp = my_to_string(i);
        std::cout << tmp << "\n";
    }
}

When run against a compiler, this code doesn't produce any warnings or errors, even with the all warnings enabled. However, when run against cppcheck, we can see:

1
2
3
$ cppcheck --enable=warning  leak.cpp
Checking leak.cpp ...
[leak.cpp:12]: (error) Memory leak: tmp

Out of Bounds

Out of bounds memory access can cause issues such as a seg fault or leaking of sensitive data. Using C++ check can help to prevent OOB errors from reaching your final release. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <array>
#include <iostream>
 
const int SZ = 5;
 
std::array<int, SZ> array_oob() {
    std::array<int, SZ> arr;
    for(int i = 0; i <= SZ; ++i) {
        arr[i] = i;
    }
    return arr;
}
 
void carray_oob() {
    int arr[5];
    for(int i = 0; i <= SZ; ++i) {
        arr[i] = i;
    }
}
 
int from_outside(int c) {
    std::array<int, SZ> arr;
    return arr[c];
}
 
void use_from_ouside() {
    from_outside(100);
}

Will produce the following errors when run with Cppcheck:

1
2
3
4
5
$ cppcheck --enable=warning oob.cpp
Checking oob.cpp ...
[oob.cpp:9]: (error) Array 'arr[5]' accessed at index 5, which is out of bounds.
[oob.cpp:17]: (error) Array 'arr[5]' accessed at index 5, which is out of bounds.
[oob.cpp:23]: (error) Array 'arr[5]' accessed at index 100, which is out of bounds.

Interestingly using Cppcheck v1.85 the following code does not produce any error:

1
2
3
4
5
6
7
std::vector<int> vector_oob() {
    std::vector<int> arr;
    for(int i = 0; i <= SZ; ++i) {
        arr[i] = i;
    }
    return arr;
}

Use After Move

Cppcheck will detect the incorrect usage of a moved from variable in the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <string>
#include <iostream>
 
void print(std::string&& to_print) {
    std::string moved(std::move(to_print));
    std::cout << moved << "\n";
}
 
void use_after_move() {
    std::string hello = "Hello";
    print(std::move(hello));
    hello[0] = 'h';
    std::cout << hello << "\n";
}

With the following error:

1
2
3
4
$ cppcheck --enable=warning uam.cpp
Checking uam.cpp ...
[uam.cpp:13]: (warning) Access of moved variable 'hello'.
[uam.cpp:14]: (warning) Access of moved variable 'hello'.

No comments:

Post a Comment