Skip to content

Commit 20d8e7b

Browse files
authored
Merge pull request #75 from luavela/imun/buristan-fix-lj_tab_newkey-for-dead-slots
Fix moving dead slots in FFI finalizers table
2 parents ead2c14 + 30e7c97 commit 20d8e7b

File tree

3 files changed

+103
-1
lines changed

3 files changed

+103
-1
lines changed

src/lj_tab.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,8 @@ TValue* lj_tab_newkey(lua_State *L, GCtab *t, const TValue *key) {
594594
n = freenode;
595595
}
596596
}
597-
copyTV(L, &n->key, key);
597+
/* XXX: Do not use copyTV here: key may be dead (e.g. while rehashing). */
598+
n->key = *key;
598599
if (LJ_UNLIKELY(tvismzero(&n->key))) { setrawV(&n->key, 0); }
599600
lj_gc_anybarriert(L, t);
600601
lua_assert(tvisnil(&n->val));
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
-- This is a part of uJIT's testing suite.
2+
-- Copyright (C) 2020-2025 LuaVela Authors. See Copyright Notice in COPYRIGHT
3+
-- Copyright (C) 2015-2020 IPONWEB Ltd. See Copyright Notice in COPYRIGHT
4+
5+
local ffi = require('ffi')
6+
pcall(jit.off)
7+
8+
ffi.cdef([[
9+
struct old {int a;};
10+
struct new {int a;};
11+
]])
12+
13+
local anchor = 0
14+
local function create_fin(i)
15+
return function()
16+
anchor = anchor + i
17+
end
18+
end
19+
20+
-- Start GC and collect the existing garbage.
21+
collectgarbage('collect')
22+
-- Make GC collect as aggressive as possible.
23+
collectgarbage('setstepmul', 100)
24+
25+
-- Create and mark GCcdata object as dead.
26+
-- As a result, ctype_state->finalizer->hmask is 1 (i.e. 2 fields):
27+
-- * __mode = "k";
28+
-- * finalizer for old GCcdata.
29+
local old_type = ffi.metatype('struct old', { __gc = create_fin(1) })
30+
local _ = old_type(1)
31+
_ = nil
32+
33+
-- Reset the GC related counters.
34+
ujit.getmetrics()
35+
-- Finish all GCSpropagate steps and GCSatomic step.
36+
while ujit.getmetrics().gc_steps_sweepstring == 0 do
37+
collectgarbage('step')
38+
end
39+
40+
-- Freeze GC at GCSsweepstring phase.
41+
collectgarbage('stop')
42+
-- Create another GCcdata object.
43+
-- As a result, ctype_state->finalizer->hmask is 3 (i.e. 4 fields):
44+
-- * __mode = "k";
45+
-- * finalizer for the old (and dead) GCcdata;
46+
-- * finalizer for the new GCcdata.
47+
-- Hence, reallocation is required
48+
local new_type = ffi.metatype('struct new', { __gc = create_fin(1) })
49+
-- Reallocation occurs and the dead GCcdata is copied to the new hpart.
50+
_ = new_type(1)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/perl
2+
#
3+
# Tests for FFI machinery and related patches.
4+
# Copyright (C) 2020-2025 LuaVela Authors. See Copyright Notice in COPYRIGHT
5+
# Copyright (C) 2015-2020 IPONWEB Ltd. See Copyright Notice in COPYRIGHT
6+
7+
use 5.010;
8+
use warnings;
9+
use strict;
10+
use lib './lib';
11+
12+
use UJit::Test;
13+
14+
sub _run_tests_group {
15+
my ($extlib_func_name, $tester, @tests) = @_;
16+
17+
foreach my $test (@tests) {
18+
my %asserts = %{ $test->{asserts} };
19+
20+
my $run_result = $tester->run($test->{file},
21+
lua_args => $extlib_func_name);
22+
23+
while (my($check, $expr_list) = each %asserts) {
24+
if (scalar @{ $expr_list}) {
25+
$run_result->$check($_) for (@{ $expr_list });
26+
} else {
27+
$run_result->$check();
28+
}
29+
}
30+
}
31+
}
32+
33+
sub run_tests {
34+
my ($tester, $tests) = @_;
35+
36+
while (my ($extlib_func_name, $tests_group) = each %{ $tests }) {
37+
_run_tests_group($extlib_func_name, $tester, @{ $tests_group });
38+
}
39+
}
40+
41+
my $tester = UJit::Test->new(
42+
chunks_dir => './chunks/ffi',
43+
);
44+
45+
my @tarray = (
46+
'gcfin_table_reallocation.lua',
47+
);
48+
49+
$tester->run($_)->exit_ok() for (@tarray);
50+
51+
exit;

0 commit comments

Comments
 (0)