简体   繁体   中英

How can I detect off-by-one errors (OBOEs) in C++ code?

Consider this simple program:

#include <array>
#include <iostream>
#include <cstdlib>

int main(int argc, char* argv[]) {
    std::array<int, 5> arr = {0, 1, 2, 3, 4};
    int idx = std::atoi(argv[1]);
    int val = std::atoi(argv[2]);

    arr[idx] = val;
    for (auto i=0u; i <= idx; i++) {
        std::cout << "arr[" << i << "] = " << arr[i] << std::endl;
    }
}

If I compile and link it with GCC 6.3.1 like this:

g++ -O0 -std=gnu++14 -fsanitize=undefined example.cpp

and run it like this:

a.out 5 98

I don't get any warnings, even though I am writing and reading one element past the end of 'arr' (the array ends at index 4).

If I run:

a.out 6 98

I receive a warning that 'index 6 out of bounds for type 'int [5]'. In this case I am writing and reading two elements past the end of 'arr'.

Why doesn't the off-by-one case throw an error? I guess because one element past the end of an array is a valid memory address (ie iterators can point to it?). Can you suggest any other tool that can reliably detect out of bounds access by one element?

EDIT

I can also run:

g++ -O0 -std=gnu++14 -fsanitize=bounds-strict example.cpp

And I see the same behaviour, ie out of bounds access by one element does not trigger a warning, but, out of bounds by two elements does. This is the part I'm trying to understand.

Why doesn't the off-by-one case throw an error?

Because -fsanitize=undefined doesn't detect out of bounds accesses.

Can you suggest any other tool that can reliably detect out of bounds access by one element?

I suggest gcc with -fsanitize=address .

$ g++ -O0 -std=gnu++14 -fsanitize=address 1.cpp
$ ./a.out  6 98
=================================================================
==119887==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd3b1aa4b8 at pc 0x560be273c4e9 bp 0x7ffd3b1aa450 sp 0x7ffd3b1aa440
WRITE of size 4 at 0x7ffd3b1aa4b8 thread T0
    #0 0x560be273c4e8 in main (/tmp/a.out+0x14e8)
    #1 0x7f9dafafe001 in __libc_start_main (/usr/lib/libc.so.6+0x27001)
    #2 0x560be273c17d in _start (/tmp/a.out+0x117d)
...

The options are documented ingcc instrumentations options .

Side note: In one of my projects, I use -fsanitize=address -fsanitize=undefined -fsanitize=leak -fsanitize=pointer-subtract -fsanitize=pointer-compare -fno-omit-frame-pointer -fstack-protector-all -fstack-clash-protection -fcf-protection .

If you want bounds check use array::at

std::array<T,N>::at : See

#include <array>
#include <iostream>
#include <cstdlib>

int main(int argc, char* argv[]) 
{
    std::array<int, 5> arr = {0, 1, 2, 3, 4};
    int idx = std::atoi(argv[1]);
    int val = std::atoi(argv[2]);

    try
    {
       arr.at(idx) = val; 
    }
    catch(std::out_of_range var)
    {
        std::cout<<"Out of bounds"<<std::endl;
    }

    for (auto i=0u; i <= idx; i++) 
    {
        try
        {
            std::cout << "arr[" << i << "] = " << arr.at(i) << std::endl;
        }
        catch(std::out_of_range var)
        {
            std::cout<<"Out of bounds"<<std::endl;
        }
    }
}

Output:

Out of bounds                                                                                                                 
arr[0] = 0                                                                                                                    
arr[1] = 1                                                                                                                    
arr[2] = 2                                                                                                                    
arr[3] = 3                                                                                                                    
arr[4] = 4 
Out of bounds 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM