|
Bugtraq
mailing list archives
Re: Format String Attacks
From: Ajax <ajax () FIREST0RM ORG>
Date: Fri, 22 Sep 2000 14:48:55 -0500
Yeah, i thought varargs got passed on the stack. Apologies, it's been a
long week, but I think I'm still right, kinda...
When the compiler constructs the va_list that ap will point to, it simply
makes the last element NULL, which can (ought) never be a valid pointer to
anything. ap then looks like:
methinks this is more suitable for comp.lang.c, but what the heck...
we'll let Elias moderate this. :=)
You cannot assume anything about va_list based on its definition or
prototypes. va_list and va_*() macros are to be treated as black-boxes. Use
them, but do not assume anything about how they are implemented or how it
works.
I'll quote you on that in a second.
For example, on the x86, all args are passed on the stack. If you're calling
printf("help %i %s %f\n", 123, "abc", 2.35432):
<=== remainder of the stack
---------------
| 2.3542 | <=== 3rd vararg, a float
---------------
| char "abc\0"| <=== 2nd vararg, a ptr to a character string
---------------
| 123 | <=== first varable arg, in this case an integer
---------------
| const char* | <=== first arg of printf(), a ptr to the fmt string
---------------
| ret address | <=== return address, duh
---------------
| ..... | <=== SP upon entry to printf()
As you can see, there are no pointers to the integer or float vararg. varargs
is similar to an array of Visual Basic-like variant types, but since one
can't have an array of voids in C, the closest thing is an array of void
pointers.
In this example, when you call va_start(), "ap" is set to point at the first
vararg, which is the integer 123. When you fetch that int using va_arg()
macro, it increments "ap" by the size of the integer.
So, we're supposed to treat va* as black boxes, right?
The compiler emits the code that pushes everything onto the stack. It
could emit some code that keeps a count of, if not the number of objects
it pushed, then at least the size of the array for perverse x86-like
architectures. Maybe put this in some process- or thread-specific
copy-on-write page that the C library maintains on process entry. Make
va_arg decrement the counter. Implement va_last accordingly, maybe
passing it the expected "next type". Even if we cons our own va_list up,
the compiler still has to push everything, so it can always keep something
of a count.
This should not break any ABI, since the count variable is not kept in a
register or in the stack. You can always use #ifdef. It might require
modifying libgcc or crt0.o a bit, but we shouldn't _need_ to touch any
system library. And we can always make the new vararg entry code
dependent on the presence of va_last, should that happen to break
something.
.over.enthusiastic.
-=:[ ajax (firest0rm)
By Date
By Thread
Current thread:
- Re: Format String Attacks, (continued)
|