views:

165

answers:

5

Do strong types, in this case char prevent buffer overflow?

char a[100]
char b[100]

strcpy(a,unknownFunction); // unknownFunction could overflow b 
                           // since its length is unknown

strcpy(b,a); // can b still overflow a with its now, 
             // potentially overflowed size?
+8  A: 

No. strcpy() just keeps going until it finds a null-terminator ('\0'). If b[] doesn't contain one, it will just walk through random memory until it eventually finds one.

Oli Charlesworth
Not random memory - whatever follows b in the address space of the process. And it may fault before finding a null byte, if this or a is close to the end of a page.
Steve Townsend
@Steve: Indeed. And what follows b in the address space of the process could easily be the return address in the stack frame. Hello exploit!
Ken Simon
@Steve: by "random", I meant memory with random contents (as far as `strcpy` is concerned)...
Oli Charlesworth
A: 

In general strncpy is a safer alternative to strcpy.

grokus
No it isn't. It just has a different easy-to-miss edge case which results in out-of-bounds memory access.
Steve Jessop
@Steve: I'm trying to figure out what this is. Is it that if your source string is too long, your destination string won't end up with a null terminator?
Oli Charlesworth
@Oli: that's the the one, yes. It means that in precisely the situations where you would have overrun on write with `strcpy`, with `strncpy` you will instead overrun on read some time later. That may or may not reduce the severity of the bug. Even with `strlcpy` (or a hand-rolled function that does `strncpy` and then writes a NUL byte), if you don't check for truncation the program might well enter a state that the programmer didn't anticipate, and which causes bugs, crashes, or exploitable security flaws. Calling this "safer" just stops people addressing string handling properly.
Steve Jessop
Where "properly" is either: (1) do absolutely minimal string handling, and get the buffer sizes right, or (2) use a string library (even one you write yourself) that allocates whatever space is needed.
Steve Jessop
+1  A: 

it can still overflow, strcpy stops when it finds a NUL to prevent an overflow, you should use strlcpy or strncpy

// using strlcpy
strlcpy(a, unknownFunction, 100);

// using strncpy
strncpy(a, unknownFunction, 100); 
a[99] = 0; // strncpy doesn't NUL-terminate the result
Zydeco
+3  A: 

The type of the arguments of strcopy() is char *. This means that once you pass your arrays to strcpy(), it has no way of knowing that these are fixed size arrays. As far as the function is concerned, they are just pointers, and it will keep copying elements until it finds \0 in b.

The main point is that once a function takes an argument that is a pointer, it has no way to determine whether it is a pointer to a single object, a pointer to a dynamically allocated buffer, or a fixed size array allocated on the stack.

So the answer is "no".

Dima
What if I passed char rather than char *?
Kevin
@Kevin: then it wouldn't compile. You can either pass `char *` or `char[]`, which is then converted to `char *`.
Dima
+4  A: 

C does not have a strong type system.

C++ is somewhat stronger typed, but is not really a true strong type system, since it has reinterpret_cast.

For a type system to prevent buffer overflow, the type information must either (1) denote an arbitrarily long string or (2) encode the buffer length in the type itself. Further, the type judgment system should ensure buffer-length to be less than or equal to for conversions.

Edit:

With some care, and neglecting the cast-tastic abilities of C++, you can write a "reasonably strong" no-overflow buffer class in C++. However, this is not strongly typed per the general definition of the term, since it is possible to attempt to access the buffer at an invalid point and still have it compile. Someone much better at templates than I might be able to write a truly template-typed SafeBuffer.

Here's my cut at it:

template<int Length>
class SafeBuffer
{
    unsigned char[Length];
public:
    unsigned char& operator[](int index); //when implemented, throws exception on out-of-range access.
};

SafeBuffer<10> buf, foo;
SafeBuffer<9> bar;
buf = foo; //pass
buf = bar; //compile-time error.
buf[100]; //compiles, but generates error at runtime. 

Note that we are leveraging the type judgment system of templates to force the compile error of buf = bar. That is an example of what a strongly typed system can do (Also note that casts can 100% destroy the typing - in C++).

Paul Nathan
+1 for pointing out delicately that the real answer to the question as asked is "unask the question".
Derrick Turk
No, it's not *unask* the question, it's simply that there was just a lot of ignorance. I went for many years without understanding type systems - it took a semester of graduate coursework to get a handle on it for me.
Paul Nathan