[16890] in bugtraq
Re: Format String Attacks
daemon@ATHENA.MIT.EDU (Ajax)
Mon Sep 25 12:02:37 2000
MIME-Version: 1.0
Content-Type: TEXT/PLAIN; charset=US-ASCII
Message-ID: <Pine.BSO.4.10.10009221432270.29475-100000@firest0rm.org>
Date: Fri, 22 Sep 2000 14:48:55 -0500
Reply-To: Ajax <ajax@FIREST0RM.ORG>
From: Ajax <ajax@FIREST0RM.ORG>
X-To: jsl2@jeditech.com
To: BUGTRAQ@SECURITYFOCUS.COM
In-Reply-To: <Pine.GSO.4.10.10009211300490.15239-100000@stargazer.jeditech.com>
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)