Skip to content

Commit ec9d188

Browse files
Mike PallBuristan
authored andcommitted
FFI/MacOS: Fix calling convention for on-stack varargs.
Thanks to Sergey Kaplun. (cherry picked from commit a2bde60) This commit fixes the regression introduced by the commit 82ca684 ("FFI/MacOS: Fix calling convention for enums."). The `isva` flag is set to 0 even for vararg functions. Thus, arguments on the stack may be aligned incorrectly, leading to the crash. This patch fixes the behaviour by adjusting the flag value instead of resetting it. In addition to the original test, various tests have been added to cover the behaviour of the vararg FFI calls. Sergey Kaplun: * added the description and the test for the problem Part of tarantool/tarantool#12480 Reviewed-by: Evgeniy Temirgaleev <e.temirgaleev@tarantool.org> Reviewed-by: Sergey Bronnikov <sergeyb@tarantool.org> Signed-off-by: Sergey Kaplun <skaplun@tarantool.org>
1 parent 6f31297 commit ec9d188

2 files changed

Lines changed: 113 additions & 1 deletion

File tree

src/lj_ccall.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1082,7 +1082,7 @@ static int ccall_set_args(lua_State *L, CTState *cts, CType *ct,
10821082
if (CCALL_ALIGN_STACKARG) { /* Align argument on stack. */
10831083
MSize align = (1u << ctype_align(ccall_struct_align(cts, d))) - 1;
10841084
#if LJ_TARGET_ARM64 && LJ_TARGET_OSX
1085-
isva = ctype_isstruct(d->info);
1085+
isva |= ctype_isstruct(d->info);
10861086
#endif
10871087
if (rp || (CCALL_PACK_STACKARG && isva && align < CTSIZE_PTR-1))
10881088
align = CTSIZE_PTR-1;
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
local ffi = require('ffi')
2+
local tap = require('tap')
3+
4+
-- The test file to test various FFI C vararg calls.
5+
-- luacheck: push no max_comment_line_length
6+
-- Originated from: https://github.com/neovim/neovim/blob/a5aa62e37b82214a1d4d1e0a54d193b155fb340c/test/unit/strings_spec.lua
7+
-- luacheck: pop
8+
-- See also: https://github.com/LuaJIT/LuaJIT/issues/1455.
9+
10+
local test = tap.test('lj-1455-macos-arm64-vararg-regression')
11+
12+
test:plan(45)
13+
14+
ffi.cdef('int sprintf(char *str, const char *format, ...);')
15+
16+
local buf = ffi.new('char[64]')
17+
18+
local function t(expected, fmt, ...)
19+
local args = {...}
20+
local ctx = string.format('sprintf(buf, "%s"', fmt)
21+
for _, x in ipairs(args) do
22+
ctx = ctx .. ', ' .. tostring(x)
23+
end
24+
ctx = ctx .. string.format(') = %s', expected)
25+
26+
test:test(ctx, function(subtest, ...)
27+
subtest:plan(2)
28+
subtest:is(ffi.C.sprintf(buf, fmt, ...), #expected,
29+
ctx .. ' - return status')
30+
subtest:is(ffi.string(buf), expected, ctx .. ' - result string')
31+
end, ...)
32+
end
33+
34+
local function i(n)
35+
return ffi.cast('int', n)
36+
end
37+
38+
local function l(n)
39+
return ffi.cast('long', n)
40+
end
41+
42+
local function ll(n)
43+
return ffi.cast('long long', n)
44+
end
45+
46+
local function z(n)
47+
return ffi.cast('ptrdiff_t', n)
48+
end
49+
50+
local function u(n)
51+
return ffi.cast('unsigned', n)
52+
end
53+
54+
local function ul(n)
55+
return ffi.cast('unsigned long', n)
56+
end
57+
58+
local function ull(n)
59+
return ffi.cast('unsigned long long', n)
60+
end
61+
62+
local function uz(n)
63+
return ffi.cast('size_t', n)
64+
end
65+
66+
t('1234567', '%d', i(1234567))
67+
t('1234567', '%ld', l(1234567))
68+
t(' 1234567', '%9ld', l(1234567))
69+
t('1234567 ', '%-9ld', l(1234567))
70+
t('deadbeef', '%x', u(0xdeadbeef))
71+
t('one two', '%s %s', 'one', 'two')
72+
t('1.234000', '%f', 1.234)
73+
t('1.234000e+00', '%e', 1.234)
74+
t('inf', '%f', 1.0 / 0.0)
75+
t('-inf', '%f', -1.0 / 0.0)
76+
t('-0.000000', '%f', tonumber('-0.0'))
77+
t('%%%', '%%%%%%')
78+
t('0x87654321', '%p', ffi.cast('char *', 0x87654321))
79+
t('0x0087654321', '%012p', ffi.cast('char *', 0x87654321))
80+
t('1234567 ', '%1$*2$ld', l(1234567), i(-9))
81+
t('1234567 ', '%1$*2$.*3$ld', l(1234567), i(-9), i(5))
82+
t('1234567 ', '%1$*3$.*2$ld', l(1234567), i(5), i(-9))
83+
t('1234567 ', '%3$*1$.*2$ld', i(-9), i(5), l(1234567))
84+
t('1234567', '%1$ld', l(1234567))
85+
t(' 1234567', '%1$*2$ld', l(1234567), i(9))
86+
t('9 12345 7654321', '%2$ld %1$d %3$lu', i(12345), l(9), ul(7654321))
87+
t('9 1234567 7654321', '%2$d %1$ld %3$lu', l(1234567), i(9), ul(7654321))
88+
t('9 1234567 7654321', '%2$d %1$lld %3$lu', ll(1234567), i(9), ul(7654321))
89+
t('9 12345 7654321', '%2$ld %1$u %3$lu', u(12345), l(9), ul(7654321))
90+
t('9 1234567 7654321', '%2$d %1$lu %3$lu', ul(1234567), i(9), ul(7654321))
91+
t('9 1234567 7654321', '%2$d %1$llu %3$lu', ull(1234567), i(9), ul(7654321))
92+
t('9 deadbeef 7654321', '%2$d %1$x %3$lu', u(0xdeadbeef), i(9), ul(7654321))
93+
t('9 c 7654321', '%2$ld %1$c %3$lu', i(('c'):byte()), l(9), ul(7654321))
94+
t('9 hi 7654321', '%2$ld %1$s %3$lu', 'hi', l(9), ul(7654321))
95+
t('9 0.000000e+00 7654321', '%2$ld %1$e %3$lu', 0.0, l(9), ul(7654321))
96+
t('two one two', '%2$s %1$s %2$s', 'one', 'two', 'three')
97+
t('three one two', '%3$s %1$s %2$s', 'one', 'two', 'three')
98+
t('1234567', '%1$d', i(1234567))
99+
t('deadbeef', '%1$x', u(0xdeadbeef))
100+
t('one two', '%1$s %2$s', 'one', 'two')
101+
t('two one', '%2$s %1$s', 'one', 'two')
102+
t('1.234000', '%1$f', 1.234)
103+
t('1.234000e+00', '%1$e', 1.234)
104+
t('inf', '%1$f', 1.0 / 0.0)
105+
t('-inf', '%1$f', -1.0 / 0.0)
106+
t('-0.000000', '%1$f', tonumber('-0.0'))
107+
t('-1234567 -7654321', '%zd %zd', z(-1234567), z(-7654321))
108+
t('-7654321 -1234567', '%2$zd %1$zd', z(-1234567), z(-7654321))
109+
t('1234567 7654321', '%zu %zu', uz(1234567), uz(7654321))
110+
t('7654321 1234567', '%2$zu %1$zu', uz(1234567), uz(7654321))
111+
112+
test:done(true)

0 commit comments

Comments
 (0)