<HTML><BODY><div class="cl-jglbkvltqk"><div>Hi, Sergey! Thanks for the patch! <br><br>LGTM after updating the test. I tried the test and it passed without the fix.</div><div> </div><div>To check container bit size calculation we can add a field at bit 16. It’s byte offset with invalid calculation will be 0 and it will be 2 with the fixed calculation.</div><div> </div><div>The output of the test with check printing:</div><div><div><div> </div><div>Without the fix:</div><div> </div><div>TAP version 13</div><div>1..2</div><div>XX-R: 4</div><div>ok - correct 0-initialization</div><div>XX: 4</div><div>XX-R: 4</div><div>ok - bitfield set correctly</div><div># align=2 sz=4</div><div># bitfield ofs=0 bitofs=0 bitsize=1</div><div># bitfield15 ofs=0 bitofs=1 bitsize=15</div><div># bitfield16 ofs=0 bitofs=16 bitsize=1</div><div> </div><div>With the fix:</div><div> </div><div><div><div>TAP version 13</div><div>1..2</div><div>XX-R: 2</div><div>ok - correct 0-initialization</div><div>XX: 2</div><div>XX-R: 2</div><div>ok - bitfield set correctly</div><div># align=2 sz=4</div><div># bitfield ofs=0 bitofs=0 bitsize=1</div><div># bitfield15 ofs=0 bitofs=1 bitsize=15</div><div># bitfield16 ofs=2 bitofs=0 bitsize=1</div></div></div></div></div><div> </div><div>The update for the code with check prints:</div><div> </div><div><div><div>diff --git a/src/lj_cconv.c b/src/lj_cconv.c</div><div>index 94ca93bb..26597aa5 100644</div><div>--- a/src/lj_cconv.c</div><div>+++ b/src/lj_cconv.c</div><div>@@ -429,6 +429,7 @@ int lj_cconv_tv_bf(CTState *cts, CType *s, TValue *o, uint8_t *sp)</div><div>   uint32_t val;</div><div>   lj_assertCTS(ctype_isbitfield(info), "bitfield expected");</div><div>   /* NYI: packed bitfields may cause misaligned reads. */</div><div>+  printf("XX-R: %u\n", ctype_bitcsz(info));</div><div>   switch (ctype_bitcsz(info)) {</div><div>   case 4: val = *(uint32_t *)sp; break;</div><div>   case 2: val = *(uint16_t *)sp; break;</div><div>@@ -666,6 +667,7 @@ void lj_cconv_bf_tv(CTState *cts, CType *d, uint8_t *dp, TValue *o)</div><div>   mask = ((1u << bsz) - 1u) << pos;</div><div>   val = (val << pos) & mask;</div><div>   /* NYI: packed bitfields may cause misaligned reads/writes. */</div><div>+  printf("XX: %u\n", ctype_bitcsz(info));</div><div>   switch (ctype_bitcsz(info)) {</div><div>   case 4: *(uint32_t *)dp = (*(uint32_t *)dp & ~mask) | (uint32_t)val; break;</div><div>   case 2: *(uint16_t *)dp = (*(uint16_t *)dp & ~mask) | (uint16_t)val; break;</div><div>diff --git a/src/lj_cparse.c b/src/lj_cparse.c</div><div>index 1b3ce7ec..44c5e16d 100644</div><div>--- a/src/lj_cparse.c</div><div>+++ b/src/lj_cparse.c</div><div>@@ -1341,8 +1341,8 @@ static void cp_struct_layout(CPState *cp, CTypeID sid, CTInfo sattr)</div><div>                     CTALIGN(lj_fls(sz));</div><div>          ct->size = (bofs >> 3);  /* Store field offset. */</div><div>        } else {</div><div>-         if (csz > amask+1 && bsz <= amask+1)</div><div>-           csz = amask+1;  /* Shrink container of packed bitfield. */</div><div>+       //   if (csz > amask+1 && bsz <= amask+1)</div><div>+       //     csz = amask+1;  /* Shrink container of packed bitfield. */</div><div>          ct->info = CTINFO(CT_BITFIELD,</div><div>            (info & (CTF_QUAL|CTF_UNSIGNED|CTF_BOOL)) +</div><div>            (csz << (CTSHIFT_BITCSZ-3)) + (bsz << CTSHIFT_BITBSZ));</div><div>diff --git a/test/tarantool-tests/lj-1451-ffi-packed-bitfield.test.lua b/test/tarantool-tests/lj-1451-ffi-packed-bitfield.test.lua</div><div>index 07e53f76..bdd5c69e 100644</div><div>--- a/test/tarantool-tests/lj-1451-ffi-packed-bitfield.test.lua</div><div>+++ b/test/tarantool-tests/lj-1451-ffi-packed-bitfield.test.lua</div><div>@@ -12,6 +12,8 @@ ffi.cdef[[</div><div> #pragma pack(push, 2)</div><div> typedef struct {</div><div>   unsigned int bitfield:1;</div><div>+  unsigned int bitfield15:15;</div><div>+  unsigned int bitfield16:1;</div><div> } packed_struct;</div><div> #pragma pack(pop)</div><div> ]]</div><div>@@ -25,4 +27,16 @@ test:is(packed.bitfield, 0, 'correct 0-initialization')</div><div> packed.bitfield = 1</div><div> test:is(packed.bitfield, 1, 'bitfield set correctly')</div><div> </div><div>+local ps = ffi.typeof('packed_struct')</div><div>+test:diag('align=%d sz=%d', ffi.alignof(ps), ffi.sizeof(ps))</div><div>+local o, bo, bs</div><div>+o, bo, bs = ffi.offsetof(ps, 'bitfield')</div><div>+test:diag('bitfield ofs=%d bitofs=%d bitsize=%d', o, bo, bs)</div><div>+o, bo, bs = ffi.offsetof(ps, 'bitfield15')</div><div>+test:diag('bitfield15 ofs=%d bitofs=%d bitsize=%d', o, bo, bs)</div><div>+o, bo, bs = ffi.offsetof(ps, 'bitfield16')</div><div>+test:diag('bitfield16 ofs=%d bitofs=%d bitsize=%d', o, bo, bs)</div><div>+</div><div>+test:fail()</div><div>+</div><div> test:done(true)</div><div> </div></div></div><div> </div><div data-signature-widget="container"><div data-signature-widget="content"><div>--<br>Best regards,</div><div>Evgeniy Temirgaleev</div></div></div><br><div class="mail-quote-collapse"><blockquote style="border-left:1px solid #0857A6;margin:10px;padding:0 0 0 10px"><span>From: Sergey Kaplun <<a href="mailto:skaplun@tarantool.org">skaplun@tarantool.org</a>><br>To: Sergey Bronnikov <<a href="mailto:sergeyb@tarantool.org">sergeyb@tarantool.org</a>>, Evgeniy Temirgaleev <<a href="mailto:e.temirgaleev@tarantool.org">e.temirgaleev@tarantool.org</a>><br>Cc: tarantool-patches@dev.tarantool.org, Sergey Kaplun <<a href="mailto:skaplun@tarantool.org">skaplun@tarantool.org</a>><br>Date: Monday, July 20, 2026 3:59 PM +03:00</span><br> <div><div id=""><div class="cl-glu90pellz"><div class="js-helper_mr_css_attr js-readmsg-msg_mr_css_attr"><div id="style_17845523990463416841_mr_css_attr"><div id="style_17845523990463416841_BODY_mr_css_attr">From: Mike Pall <mike><br><br>Reported by Huang Haiyang.<br><br>(cherry picked from commit e4c7d8b38040518d42599eef8ddb5e67aa967a9c)<br><br>The bitfield of packed structure uses the original type size, which<br>leads to heap-buffer-overflow access on conversions.<br><br>This patch fixes it by adjusting the size of the bitfield container when<br>necessary.<br><br>Sergey Kaplun:<br>* added the description and the test for the problem<br><br>Part of tarantool/tarantool#12880<br>---<br><br>Branch: <a href="https://github.com/tarantool/luajit/tree/skaplun/lj-1451-ffi-packed-bitfield">https://github.com/tarantool/luajit/tree/skaplun/lj-1451-ffi-packed-bitfield</a><br>Related issues:<br>* <a href="https://github.com/LuaJIT/LuaJIT/issues/1451">https://github.com/LuaJIT/LuaJIT/issues/1451</a><br>* <a href="https://github.com/tarantool/tarantool/issues/12880">https://github.com/tarantool/tarantool/issues/12880</a><br><br>src/lj_cparse.c | 2 ++<br>.../lj-1451-ffi-packed-bitfield.test.lua | 28 +++++++++++++++++++<br>2 files changed, 30 insertions(+)<br>create mode 100644 test/tarantool-tests/lj-1451-ffi-packed-bitfield.test.lua<br><br>diff --git a/src/lj_cparse.c b/src/lj_cparse.c<br>index ff23b44b..1b3ce7ec 100644<br>--- a/src/lj_cparse.c<br>+++ b/src/lj_cparse.c<br>@@ -1341,6 +1341,8 @@ static void cp_struct_layout(CPState *cp, CTypeID sid, CTInfo sattr)<br>CTALIGN(lj_fls(sz));<br>ct->size = (bofs >> 3); /* Store field offset. */<br>} else {<br>+ if (csz > amask+1 && bsz <= amask+1)<br>+ csz = amask+1; /* Shrink container of packed bitfield. */<br>ct->info = CTINFO(CT_BITFIELD,<br>(info & (CTF_QUAL|CTF_UNSIGNED|CTF_BOOL)) +<br>(csz << (CTSHIFT_BITCSZ-3)) + (bsz << CTSHIFT_BITBSZ));<br>diff --git a/test/tarantool-tests/lj-1451-ffi-packed-bitfield.test.lua b/test/tarantool-tests/lj-1451-ffi-packed-bitfield.test.lua<br>new file mode 100644<br>index 00000000..07e53f76<br>--- /dev/null<br>+++ b/test/tarantool-tests/lj-1451-ffi-packed-bitfield.test.lua<br>@@ -0,0 +1,28 @@<br>+local tap = require('tap')<br>+<br>+-- Test file to demonstrate LuaJIT's incorrect behaviour of the<br>+-- `#pragma` pack directive for bitfields in structures.<br>+-- See also: <a href="https://github.com/LuaJIT/LuaJIT/issues/1451">https://github.com/LuaJIT/LuaJIT/issues/1451</a>.<br>+<br>+local test = tap.test('lj-1451-ffi-packed-bitfield')<br>+<br>+local ffi = require('ffi')<br>+<br>+ffi.cdef[[<br>+#pragma pack(push, 2)<br>+typedef struct {<br>+ unsigned int bitfield:1;<br>+} packed_struct;<br>+#pragma pack(pop)<br>+]]<br>+<br>+test:plan(2)<br>+<br>+local packed = ffi.new('packed_struct')<br>+<br>+test:is(packed.bitfield, 0, 'correct 0-initialization')<br>+<br>+packed.bitfield = 1<br>+test:is(packed.bitfield, 1, 'bitfield set correctly')<br>+<br>+test:done(true)<br>--<br>2.55.0</div></div></div></div></div></div></blockquote></div></div></BODY></HTML>