OSEC

Neohapsis is currently accepting applications for employment. For more information, please visit our website www.neohapsis.com or email hr@neohapsis.com
 
From: Zack Weinberg (zackwSTANFORD.EDU)
Date: Thu Apr 05 2001 - 13:51:03 CDT

  • Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]

    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