Weird problem with pointer dereferencing

R

Rui Maciel

Consider the following test program:


<code>
#include <string.h>
#include <stdio.h>


struct buffer
{
#define BUFFER_SIZE 16
char buffer[BUFFER_SIZE];
char *p; /* cursor */
char *q; /* YYMARKER */
char *marker; /* token start marker */
char *limit; /* limit marker */
};

void fill(FILE *file, struct buffer *b)
{
if(feof(file))
{
}
else if(b->p > b->limit - 10)
{
memmove(b->buffer,b->marker, b->limit - b->marker);
b->limit = b->buffer + (b->limit - b->marker) + fread(b->buffer + (b->limit
- b->marker),sizeof(char),BUFFER_SIZE-(b->limit-b->marker),file);
b->p = b->buffer + (b->p - b->marker);
b->marker = b->buffer;
*b->limit = '\0'; // <----- offending line
}
}


int main(void)
{
struct buffer b = {.p = b.buffer+BUFFER_SIZE, .q= b.buffer+BUFFER_SIZE, .marker =
b.buffer+BUFFER_SIZE, .limit = b.buffer+BUFFER_SIZE};

fill(stdin, &b);

return 0;
}
</code>


I've noticed that the pointer dereferencing done at the marked offending line not only sets
*b->limit to '\0' but also has the nasty side effect of screwing up with the address
pointed to by b->p. For example, running the example code on GDB does the following:


<gdb output>
23 b->limit = b->buffer + (b->limit - b->marker) + fread(b->buffer +
(b->limit - b->marker),sizeof(char),BUFFER_SIZE-(b->limit-b->marker),file);
(gdb)
asdf asdf asdf adsf
24 b->p = b->buffer + (b->p - b->marker);
(gdb) n
25 b->marker = b->buffer;
(gdb) n
26 *b->limit = '\0'; // <----- offending line
(gdb) print *b
$1 = {buffer = "asdf asdf asdf a", p = 0x7fffffffdce0 "asdf asdf asdf
a\340\334\377\377\377\177", q = 0x7fffffffdcf0 "\340\334\377\377\377\177",
marker = 0x7fffffffdce0 "asdf asdf asdf a\340\334\377\377\377\177", limit =
0x7fffffffdcf0 "\340\334\377\377\377\177"}
(gdb) n
28 }
(gdb) print *b
$2 = {buffer = "asdf asdf asdf a", p = 0x7fffffffdc00 "\021 d\r", q = 0x7fffffffdcf0 "",
marker = 0x7fffffffdce0 "asdf asdf asdf a", limit = 0x7fffffffdcf0 ""}
</gdb output>


So, is this supposed to happen? I was expecting that the pointer dereference would only
change the value stored in the memory address pointed to by b->limit. Why is that memory
dereferencing screwing up with b->p?


Thanks in advance,
Rui Maciel
 
A

Alan Curry

|struct buffer
|{
| #define BUFFER_SIZE 16
| char buffer[BUFFER_SIZE];
| char *p; /* cursor */
| char *q; /* YYMARKER */
| char *marker; /* token start marker */
| char *limit; /* limit marker */
|};
[...]
|(gdb) n
|26 *b->limit = '\0'; // <----- offending line
|(gdb) print *b
|$1 = {buffer = "asdf asdf asdf a", p = 0x7fffffffdce0 "asdf asdf asdf
|a\340\334\377\377\377\177", q = 0x7fffffffdcf0 "\340\334\377\377\377\177",
| marker = 0x7fffffffdce0 "asdf asdf asdf a\340\334\377\377\377\177", limit =
|0x7fffffffdcf0 "\340\334\377\377\377\177"}
|(gdb) n
|28 }
|(gdb) print *b
|$2 = {buffer = "asdf asdf asdf a", p = 0x7fffffffdc00 "\021 d\r", q =
|0x7fffffffdcf0 "",
|marker = 0x7fffffffdce0 "asdf asdf asdf a", limit = 0x7fffffffdcf0 ""}
|</gdb output>

Your struct is located at 0x7fffffffdce0. The first field, buffer, is also
located at 0x7fffffffdce0. Its size is 16. Your b->limit pointer is
0x7fffffffdcf0, also known as 0x7fffffffdce0+16 or &buffer[16]. It's outside
the bounds of the array. Dereferencing b->limit is a classic buffer overflow
error, and writing to it caused memory corruption elsewhere.

The "elsewhere" happened to be b->p because the p field immediately follows
the buffer, and there was no padding between them.
 
I

Ike Naar

#include <string.h>
#include <stdio.h>

struct buffer
{
#define BUFFER_SIZE 16
char buffer[BUFFER_SIZE];
char *p; /* cursor */
char *q; /* YYMARKER */
char *marker; /* token start marker */
char *limit; /* limit marker */
};

void fill(FILE *file, struct buffer *b)
{
if(feof(file))
{
}
else if(b->p > b->limit - 10)
{
memmove(b->buffer,b->marker, b->limit - b->marker);
b->limit = b->buffer + (b->limit - b->marker) +
fread(b->buffer + (b->limit
- b->marker),sizeof(char),BUFFER_SIZE-(b->limit-b->marker),file);
b->p = b->buffer + (b->p - b->marker);
b->marker = b->buffer;
*b->limit = '\0'; // <----- offending line
}
}


int main(void)
{
struct buffer b = {.p = b.buffer+BUFFER_SIZE, .q=
b.buffer+BUFFER_SIZE, .marker =
b.buffer+BUFFER_SIZE, .limit = b.buffer+BUFFER_SIZE};

fill(stdin, &b);

return 0;
}
</code>


I've noticed that the pointer dereferencing done at the marked offending
line not only sets
*b->limit to '\0' but also has the nasty side effect of screwing up with
the address pointed to by b->p.

You are writing outside the buffer; it is only BUFFER_SIZE characters
long, but you're writing a '\0' on top of the (BUFFER_SIZE+1)'st character.
 
R

Rui Maciel

Alan said:
Your struct is located at 0x7fffffffdce0. The first field, buffer, is also
located at 0x7fffffffdce0. Its size is 16. Your b->limit pointer is
0x7fffffffdcf0, also known as 0x7fffffffdce0+16 or &buffer[16]. It's
outside the bounds of the array. Dereferencing b->limit is a classic
buffer overflow error, and writing to it caused memory corruption
elsewhere.

The "elsewhere" happened to be b->p because the p field immediately
follows the buffer, and there was no padding between them.

You are absolutely right. The dreaded "off-by-one" error... Shame on me.


Thanks for the help,
Rui Maciel
 
M

Morris Keesan

void fill(FILE *file, struct buffer *b)
{
if(feof(file))
{
}
else if(b->p > b->limit - 10)
{ ....
....
,file);

In addition to the off-by-one error that caused the pointer problem you
were looking for, this code exhibits a very common misunderstanding of
end-of-file processing in C. feof(file) returns true when a PREVIOUS
attempt to read from the file has encountered an end of file condition.
If feof(file) returns false, this tells you nothing at all about whether
the file pointer is currently at the end of the file, causing the next
read to fail.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,989
Messages
2,570,207
Members
46,782
Latest member
ThomasGex

Latest Threads

Top