| Age | Commit message (Collapse) | Author |
|
[ci skip]
|
|
Fix SEGV on re-raising NoMemoryError
|
|
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
|
|
Update license year range to 2016
|
|
|
|
Fix passing all zero string to Kernel#Integer
|
|
|
|
|
|
jefffederman/feature/install-mruby-with-ruby-install
Update README to mention ruby version management tools
|
|
|
|
Fix build error in ruby 1.8.
|
|
Apply change of #2978 to :clang, :gcc, :visualcpp toolchains
|
|
Fix class variable reference in module
|
|
Fix #3079
|
|
|
|
Fix segfault on mrb_exc_backtrace.
|
|
The code to iterate over backtrace locations was changed in #3065, but
unfortunately output_backtrace was not correctly updated to forward the
callback.
|
|
|
|
|
|
|
|
|
|
|
|
printf precision parameter must be 'int' type
|
|
There is a problem when MRB_INT64 is enabled.
|
|
symname_p support `!~`
|
|
provide macro to ease using printf mrb_int
|
|
|
|
|
|
character boundary; ref #3067
that means String#index matches first byte of a multi-byte character. this behavior is different
from CRuby, but a compromise for mruby which does not have encoding stuffs.
|
|
|
|
|
|
|
|
mrb_funcall(); close #3056
|
|
use _setjmp/_longjmp on OS X
|
|
|
|
|
|
Fiber.yield cannot be called from #initialize which is called by
mrb_funcall(). It is mruby limitation.
|
|
Use memchr for performance
|
|
```ruby
s = "b"
str = ("a" * 100 + s)
t = Time.now
str.index(s)
puts Time.now - t
```
before => 0.000788
after => 0.000508
---
```ruby
s = "b"
str = ("a" * 100 * 1024 * 1024 + s)
t = Time.now
str.index(s)
puts Time.now - t
```
before => 0.225474
after => 0.008658
|
|
|
|
|
|
|
|
Support backtrace after method calls
|
|
|
|
GitHub: fix #2902, #2917
The current implementation traverses stack to retrieve backtrace. But
stack will be changed when some operations are occurred. It means that
backtrace may be broken after some operations.
This change (1) saves the minimum information to retrieve backtrace when
exception is raised and (2) restores backtrace from the minimum
information when backtrace is needed. It reduces overhead for creating
backtrace Ruby objects.
The space for the minimum information is reused by multiple
exceptions. So memory allocation isn't occurred for each exception.
|
|
Fix indent
|
|
|
|
Improve `Struct` test.
|
|
|
|
Refactor Array#each
|