#! /usr/bin/stap -g

global added%
global trap = 1  /* stap -G trap=0 to only trace, not fix */

/* CVE-2015-0235 "ghostbuster" band-aid

Works by incrementing the size_needed variable set around line 86
of glibc nss/digits_dots.c, so as to account for the missing sizeof
(*h_alias_ptr).  This makes the subsequent comparisons work and
return error codes for buffer-overflow situations.

GLIBC DWARF debuginfo is needed, because we insert a statement
probe just after the initial assignment.  stap -g (guru mode)
is needed because we're modifying state. 

85
86   size_needed = (sizeof (*host_addr)
87                 + sizeof (*h_addr_ptrs) + strlen (name) + 1);
88
89   if (buffer_size == NULL)
90     {
91       if (buflen < size_needed)
92         {
93           if (h_errnop != NULL)
94           *h_errnop = TRY_AGAIN;
95           __set_errno (ERANGE);
96           goto done;
97         }
98     }
99   else if (buffer_size != NULL && *buffer_size < size_needed)
100    {
101      char *new_buf;
102      *buffer_size = size_needed;
103      new_buf = (char *) realloc (*buffer, *buffer_size);
*/


probe process("/lib*/libc.so.6") /* Adjust wildcard for your distro. */
      .statement("__nss_hostname_digits_dots@*:87-102")
      /* We use a range here because optimized glibc may only have
         a few clear-cut PC-ranges for statements.  This particular
         line range is unusually reliable, because the digits_dots.c
         file has seen very little change in glibc, up until the
         CVE bug fix.  We use the added[] array to make sure we only
         increment once per thread per function invocation. */
{
  if (! added[tid()])
    {
      added[tid()] = 1; # we only want to add once
      printf("%s[%d] BOO! size_needed=%d ", execname(), tid(), $size_needed)
      if (trap)
        {
          /* The &@cast() business is a fancy sizeof(uintptr_t),
           which makes this script work for both 32- and 64-bit glibc's. */
          $size_needed = $size_needed + &@cast(0, "uintptr_t")[1]
          printf("ghostbusted to %d", $size_needed)
        }
      printf("\n")
    }
}

probe process("/lib*/libc.so.6").function("__nss_hostname_digits_dots").return
{
  delete added[tid()]
}
