1. 20 Jan, 2016 1 commit
  2. 19 Jan, 2016 2 commits
    • Yukihiro "Matz" Matsumoto's avatar
      Merge pull request #3087 from kou/fix-segv-on-rerasing-no-memory-error · 17bd40a5
      Yukihiro "Matz" Matsumoto authored
      Fix SEGV on re-raising NoMemoryError
      17bd40a5
    • Kouhei Sutou's avatar
      Fix SEGV on re-raising NoMemoryError · 1d84b320
      Kouhei Sutou authored
      Think about the following Ruby script:
      
      segv.rb:
      
          begin
            lambda do
              lambda do
                "x" * 1000 # NoMemoryError
              end.call
            end.call
          rescue
            raise
          end
      
      If memory can't allocate after `"x" * 1000`, mruby crashes.
      
      Because L_RAISE: block in mrb_vm_exec() calls mrb_env_unshare() via
      cipop() and mrb_env_unshare() uses allocated memory without NULL check:
      
      L_RAISE: block:
      
          L_RAISE:
            // ...
            while (ci[0].ridx == ci[-1].ridx) {
              cipop(mrb);
              // ...
            }
      
      cipop():
      
          static void
          cipop(mrb_state *mrb)
          {
            struct mrb_context *c = mrb->c;
      
            if (c->ci->env) {
              mrb_env_unshare(mrb, c->ci->env);
            }
      
            c->ci--;
          }
      
      mrb_env_unshare():
      
          MRB_API void
          mrb_env_unshare(mrb_state *mrb, struct REnv *e)
          {
            size_t len = (size_t)MRB_ENV_STACK_LEN(e);
            // p is NULL in this case
            mrb_value *p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len);
      
            MRB_ENV_UNSHARE_STACK(e);
            if (len > 0) {
              stack_copy(p, e->stack, len); // p is NULL but used. It causes SEGV.
            }
            e->stack = p;
            mrb_write_barrier(mrb, (struct RBasic *)e);
          }
      
      To solve the SEGV, this change always raises NoMemoryError even when
      realloc() is failed after the first NoMemoryError in
      mrb_realloc(). mrb_unv_unshare() doesn't need to check NULL with this
      change.
      
      But it causes infinite loop in the following while:
      
          L_RAISE:
            // ...
            while (ci[0].ridx == ci[-1].ridx) {
              cipop(mrb);
              // ...
            }
      
      Because cipop() never pops ci.
      
      This change includes cipop() change. The change pops ci even when
      mrb_unv_unshare() is failed by NoMemoryError.
      
      This case can be reproduced by the following program:
      
          #include <stdlib.h>
          #include <mruby.h>
          #include <mruby/compile.h>
      
          static void *
          allocf(mrb_state *mrb, void *ptr, size_t size, void *ud)
          {
            static mrb_bool always_fail = FALSE;
      
            if (size == 1001) {
              always_fail = TRUE;
            }
            if (always_fail) {
              return NULL;
            }
      
            if (size == 0) {
              free(ptr);
              return NULL;
            } else {
              return realloc(ptr, size);
            }
          }
      
          int
          main(int argc, char **argv)
          {
            mrb_state *mrb;
            mrbc_context *c;
            FILE *file;
      
            mrb = mrb_open_allocf(allocf, NULL);
            c = mrbc_context_new(mrb);
            file = fopen(argv[1], "r");
            mrb_load_file_cxt(mrb, file, c);
            fclose(file);
            mrbc_context_free(mrb, c);
            mrb_close(mrb);
      
            return EXIT_SUCCESS;
          }
      
      Try the following command lines:
      
          % cc -I include -L build/host/lib -O0 -g3 -o no-memory no-memory.c -lmruby -lm
          % ./no-memory segv.rb
      1d84b320
  3. 18 Jan, 2016 2 commits
  4. 14 Jan, 2016 3 commits
  5. 13 Jan, 2016 4 commits
  6. 11 Jan, 2016 1 commit
  7. 10 Jan, 2016 1 commit
  8. 08 Jan, 2016 1 commit
  9. 07 Jan, 2016 9 commits
  10. 06 Jan, 2016 4 commits
  11. 05 Jan, 2016 1 commit
  12. 04 Jan, 2016 1 commit
  13. 02 Jan, 2016 3 commits
  14. 01 Jan, 2016 1 commit
  15. 31 Dec, 2015 5 commits
  16. 30 Dec, 2015 1 commit