CVE-2017-8929 (C, published 2017-05-14T22:29Z)
Description: value: The sized_string_cmp function in libyara/sizedstr.c in YARA 3.5.0 allows remote attackers to cause a denial of service (use-after-free and application crash) via a crafted rule.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-416 \u2014 Use After Free
The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
Repository: https://github.com/VirusTotal/yara
Files changed:
libyara/arena.c
libyara/exec.c
libyara/include/yara/object.h
libyara/include/yara/sizedstr.h
libyara/modules/tests.c
libyara/object.c
libyara/sizedstr.c
tests/test-rules.c
Commit message: Fix issue #658



Vulnerable code:
//    A pointer to the arena's data. NULL if the no data has been written to
        {
          r1.o = function->return_obj;
          push(r1);
        }
        else
        {
          stop = TRUE;
        }
  
  SIZED_STRING* s1,
  SIZED_STRING* s2);
      ((YR_OBJECT_INTEGER*) copy)->value = UNDEFINED;
      ((YR_OBJECT_STRING*) copy)->value = NULL;

Fixed code:
//    A pointer to the arena's data. NULL if no data has been written to
#include <yara/arena.h>
  YR_OBJECT** obj_ptr;
  YR_ARENA* obj_arena;
  FAIL_ON_ERROR_WITH_CLEANUP(
      yr_arena_create(1024, 0, &obj_arena),
      yr_free(stack));


        assert(sp == 0);
        // if i == MAX_OVERLOADED_FUNCTIONS at this point no matching
        // prototype was found, but this shouldn't happen.

        // make a copy of the returned object and push the copy into the stack
        // function->return_obj can't be pushed because it can change in
        // subsequent calls to the same function.

          result = yr_object_copy(function->return_obj, &r1.o);

        // a pointer to the copied object is stored in a arena in order to
        // free the object before exiting yr_execute_code
        if (result == ERROR_SUCCESS)
          result = yr_arena_write_data(obj_arena, &r1.o, sizeof(r1.o), NULL);

        stop = (result != ERROR_SUCCESS);
        push(r1);
  obj_ptr = (YR_OBJECT**) yr_arena_base_address(obj_arena);

  while (obj_ptr != NULL)
  {
    yr_object_destroy(*obj_ptr);

    obj_ptr = (YR_OBJECT**) yr_arena_next_address(
        obj_arena, obj_ptr, sizeof(YR_OBJECT*));
  }

  yr_arena_destroy(obj_arena);
int yr_object_copy(
    YR_OBJECT* object,
    YR_OBJECT** object_copy);



    SIZED_STRING* s1,
    SIZED_STRING* s2);


SIZED_STRING* sized_string_dup(
    SIZED_STRING* s);
define_function(foobar)
{
  int64_t arg = integer_argument(1);

  switch (arg)
  {
    case 1:
      return_string("foo");
      break;
    case 2:
      return_string("bar");
      break;
  }

  return_string("oops")
}

  declare_function("foobar", "i", "s", foobar);
      ((YR_OBJECT_INTEGER*) copy)->value = ((YR_OBJECT_INTEGER*) object)->value;
      if (((YR_OBJECT_STRING*) object)->value != NULL)
      {
        ((YR_OBJECT_STRING*) copy)->value = sized_string_dup(
            ((YR_OBJECT_STRING*) object)->value);
      }
      else
      {
        ((YR_OBJECT_STRING*) copy)->value = NULL;
      }
      break;

    case OBJECT_TYPE_FLOAT:
      ((YR_OBJECT_DOUBLE*) copy)->value = ((YR_OBJECT_DOUBLE*) object)->value;
#include <string.h>
#include <yara/mem.h>


SIZED_STRING* sized_string_dup(
    SIZED_STRING* s)
{
  SIZED_STRING* result = (SIZED_STRING*) yr_malloc(
      sizeof(SIZED_STRING) + s->length);

  if (result == NULL)
    return NULL;

  result->length = s->length;
  result->flags = s->flags;

  strncpy(result->c_string, s->c_string, s->length + 1);

  return result;
}
  assert_true_rule(
      "import \"tests\" \
       rule test { \
        condition: tests.foobar(1) == tests.foobar(1) \
      }",
      NULL);

  assert_true_rule(
      "import \"tests\" \
       rule test { \
        condition: tests.foobar(1) != tests.foobar(2) \
      }",
      NULL);