This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] Don't bind to registered ports in bindresvport


On 06/06/2012 05:49 PM, Carlos O'Donell wrote:
>  * What is stored in the alloca'd space? Is there a security issue
>  inherent in placing this information at a known offset from the
>  stack pointer? If there is then it might call for using malloc.

There is always a security issue in accessing memory, regardless of
whether one uses alloca or simply takes the address of a local
variable.  So I'm not sure why we're picking on alloca here.

>  * Is the allocated space large? The use of alloca in glibc is
>  limited and large allocations will need to be taken from malloc.

__libc_use_alloca does that.

Here is a quick cut at a proposed revised draft.

-----

Here are some things to consider when deciding whether to use
alloca or malloc.

* Do not use alloca to create an array whose size S is such that
  ! libc_use_alloca (S), as large arrays like that may bypass
  stack-overflow checking.

* If the storage may need to outlive the current function, then
  obviously alloca cannot be used.

* If the API does not allow returning a memory-allocation failure
  indication such as ENOMEM, then alloca may be preferable, as malloc
  can fail.

* If this is a hot path with a small allocation, prefer alloca,
  as it is typically much faster.

* When growing a buffer, either on the stack or on the heap,
  watch out for integer overflow when calculating the new size.
  Such overflow should be treated as allocation failure than
  letting the integer wrap around.

* If the size of the buffer is directly or indirectly under user control,
  consider imposing a maximum to help make denial-of-service attacks
  more difficult.

* If this is a hot path and the allocation size is typically small but
  may be large, and is known in advance, you can use the following
  pattern:

    bool use_alloca = __libc_use_alloca (bufsize);
    struct foo *buf = use_alloca ? alloca (bufsize) : malloc (bufsize);
    do_work_with (buf, bufsize);
    if (! use_alloca)
      free (buf);

  This use of alloca is a memory optimization.  That is, the above
  'alloca' example is equivalent to the following:

    struct foo buffer[__MAX_ALLOCA_CUTOFF / sizeof (struct foo)];
    bool use_auto = bufsize <= sizeof buffer;
    struct foo *buf = use_auto ? buffer : malloc (bufsize);
    do_work_with (buf, bufsize);
    if (! use_auto)
      free (buf);

  except that the 'alloca' version consumes only the stack space
  needed, rather than always consuming approximately
  __MAX_ALLOCA_CUTOFF bytes.

* If the amount of storage is not known in advance but may grow
  without bound, you can start with a small buffer on the stack and
  switch to malloc if it is not large enough.  For example:

    struct foo buffer[__MAX_ALLOCA_CUTOFF / sizeof (struct foo)];
    struct foo *buf = buffer;
    size_t bufsize = sizeof buffer;
    void *allocated = NULL;
    size_t needed;
    while (bufsize < (needed = do_work_with (buf, bufsize)))
      {
        buf = realloc (allocated, needed);
        if (! buf)
          {
            needed = 0;
            break;
          }
        allocated = buf;
        bufsize = needed;
      }
    free (allocated);
    return needed; /* This is zero on allocation failure.  */

* You can also grow a single buffer on the stack, so long as it does
  not get too large, by using 'extend_alloca'.  This is a memory
  optimization of the previous example, just as 'alloca' is a memory
  optimization of a local array.  For example:

    size_t bufsize = 256;
    struct foo *buf = alloca (bufsize);
    void *allocated = NULL;
    size_t needed;
    while (bufsize < (needed = do_work_with (buf, bufsize)))
      {
        if (__libc_use_alloca (needed))
          buf = extend_alloca (buf, bufsize, needed);
        else
          {
            buf = realloc (allocated, needed);
            if (! buf)
              {
                needed = 0;
                break;
              }
            bufsize = needed;
          }
      }
    free (allocated);
    return needed; /* This is zero on allocation failure.  */

* To boost performance a bit in the typical case of the above
  examples, you can use __builtin_expect, e.g.,
  "if (__builtin_expect (use_alloca, 1))" instead of just "if (use_alloca)".


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