This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: GCC extension for protecting applications from format string attacks


On Sat, Mar 31, 2001 at 11:55:36PM +0900, Makoto Iwamura wrote:
> Iwamura and Etoh would make a GCC extension for protecting applications 
> from format string attacks. The idea is as follows. Any suggestion and 
> comments will be appreciated.
...
> Actually machine codes of the function calling printf() become the 
> following code.
> Surely,<number of arguments> etc. don't affect the original action.
> 
>    .
>    .
>    .
> call printf
> jmp .L14
> .byte <mark>
> .long <number of arguments>
> .L14:
>    .
>    .
>    .

You will have trouble making this scheme work on architectures other
than x86.  There can be several problems: the one-byte mark may
interfere with proper alignment of instructions; there may be no safe
value for <mark>; __builtin_return_address may not function reliably.

In addition, on all architectures this imposes a minor performance
cost and a rather large space penalty.

I'm going to suggest an alternate scheme which should avoid these
problems.  To the best of my knowledge, all ABIs supported by GCC have
at least one call-clobbered register which is not used to pass
arguments.  You extend the calling sequence for varargs functions, so
that it places a magic value in the top half of one of these
registers, and the argument count in the bottom half.  On i386, %edx
can be used for this purpose:

	movl	$0xCAFE0003, %edx	; for example
	call	printf

You then add a builtin function, __builtin_va_count(), which checks
the register for the magic value and extracts the count if present.
If the magic value is not present, it returns (short)-1.  You are
limited to 65,534 arguments to a varargs function, which should
present no great handicap.  (C99 requires support for no more than 127
arguments to any function call.)  __builtin_va_count has to inject
code at the very beginning of the function, which is not hard - look
at expand_builtin_saveregs in builtins.c.  [Assuming that still
works.  I'm a bit confused, it looks like the support logic for the
old varargs implementation is still hanging around.]

Then your printf implementation can do something like

int
printf (const char *fmt, ...)
{
  va_list ap;
  int expected = __builtin_va_count();
  int count = 0;

  if (expected != -1) {
    const char *x;
    for (x = fmt; *x; x++) {
      if (*x == '%') {
	if (x[1] == '%') x++;
	else count++;
	/* Also, handle * in precision or field width.  */
      }
    }
    if (count > expected) {
      fprintf(stderr, "printf: %d arguments, %d specifiers\n", 
	      expected, count);
      return 0;
    }

  va_start(ap, fmt);
  /* rest of function here */
}

...
> We have several concerns to proceed further implementation. 
> 
> - There are several length of jmp instructions of different processors. We 
> have to find the mark and number of arguments correctly regardless of the 
> processor. We are worried about what MARK and SEARCH_RANGE should be.

You should not have to worry about this if you use the scratch
register suggestion instead.

> - Can we add a new built-in function in GCC?

Yes, quite easily.  Look at gcc/builtins.c.  You will also need to add
an entry to builtins.def and a call to builtin_function in c-common.c,
and possibly tweak code elsewhere.

> - When we write a printf library which protects format string attacks and 
> stops the execution of a program, we can not stop those attacks for 
> denial-of-service. Is there any idea how the program continues to the 
> execution after the detection of format string attacks?

This is trickier.  I don't know precisely how a format string attack
works, but it seems to me that you could probably stop most of them by
simply ignoring all format specifiers which require arguments beyond
the actual list.  This won't protect against an attack which does not
need to run past the end of the argument list, but I don't know if any
such attack is possible.

Don't forget about %1$s notation, which allows the user to ask for
argument 2 before argument 1, e.g.

Neither your technique nor mine can be applied to functions that take
a va_list argument, such as vfprintf.  This is unfortunate, because in
most C libraries all the other printf functions are implemented in
terms of vfprintf.  You could write your checking library to have a
special vfprintf that took an extra argument, the actual argument
count, but that would not protect user functions that call vfprintf
directly.

If you're willing to break the ABI, the argument count could be
embedded in the va_list object, and __builtin_va_count could extract
it.  For even better protection, va_arg could check its position
against the count.  It's not clear what it should do when it reaches
the limit; the C standard doesn't contemplate va_arg failing.  Perhaps
aborting is safest.

You could consider revising brand-new ABIs to incorporate this
scheme officially, e.g. the x86-64 port being developed now.

zw


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]