[๐Ÿ’Ž Diamond 1] Math.exp๊ฐ€ NaN์„ ์žŠ์„ ๋•Œ (2) โ€” ์ด ๋ฒ„๊ทธ๋ฅผ ์ฃฝ์ธ V8 11.2 ํ•˜๋“œ๋‹, ์†Œ์Šค ๋ถ€๊ฒ€ โ€” DreamHack EXP-NaN

2026-06-07ยท1๋ถ„ ์ฝ๊ธฐยท

[๐Ÿ’Ž Diamond 1] Math.exp๊ฐ€ NaN์„ ์žŠ์„ ๋•Œ (2) โ€” ์ด ๋ฒ„๊ทธ๋ฅผ ์ฃฝ์ธ V8 11.2 ํ•˜๋“œ๋‹, ์†Œ์Šค ๋ถ€๊ฒ€ โ€” DreamHack EXP-NaN

1ํŽธ์—์„œ ํƒ€์ž…ํ˜ผ๋™์„ ์ธ๋ฑ์Šค๊นŒ์ง€ ์‚ด๋ ค ๋ณด๋ƒˆ์ง€๋งŒ ๊ฑฐ๊ธฐ์„œ ํŠธ๋žฉ์œผ๋กœ ๋Š๊ฒผ๋‹ค. 2ํŽธ์€ "์™œ ํŠธ๋žฉ์ธ๊ฐ€"๋ฅผ V8 11.2 ์†Œ์Šค๋กœ ๋ถ€๊ฒ€ํ•œ๋‹ค. CheckBounds๊ฐ€ ๊ธฐ๊ณ„์–ด๊ฐ€ ๋˜๋Š” ๊ฒฝ๋กœ, typer๊ฐ€ ์ธ๋ฑ์Šค๋ฅผ in-bounds๋กœ ์ฆ๋ช…ํ•˜๋ฉด ๊ฒ€์‚ฌ๋ฅผ ์ œ๊ฑฐ๊ฐ€ ์•„๋‹ˆ๋ผ abort๋กœ ๋ฐ”๊พธ๋Š” ๋ฌด์กฐ๊ฑด ํ•˜๋“œ๋‹(kAbortOnOutOfBounds), ๊ทธ๋ฆฌ๊ณ  doar-e์‹ ์šฐํšŒ๋ฅผ ์ •์กฐ์ค€ํ•ด ๋ง‰์•„๋‘” element-access ๋นŒ๋”์˜ ๋‘ ๋ฒˆ์งธ CheckBounds๊นŒ์ง€ โ€” ์†Œ์Šค ์ฃผ์„์งธ๋กœ ๋“ค๊ณ  ๋ณธ๋‹ค. ๋์œผ๋กœ, ๊ฐ™์€ ์‹œ๊ธฐ ํ•˜๋“œ๋‹ ์ผœ์ง„ V8์„ ์‹ค์ œ๋กœ ๋šซ์€ ์ต์Šค(๊ตฌ๊ธ€ v8CTFยทopenECSC)๊ฐ€ ์™œ ๊ฐ’-range typer ๋ฒ„๊ทธ๊ฐ€ ์•„๋‹ˆ๋ผ length/element-kind๋ฅผ ์ง์ ‘ ์†์ƒ์‹œํ‚ค๋Š” ๋‹ค๋ฅธ ๋ฒ„๊ทธ ํด๋ž˜์Šค๋ฅผ ์“ฐ๋Š”์ง€ ๋Œ€์กฐํ•œ๋‹ค.

๋ฌธ์ œ: DreamHack โ€” EXP-NaN (Dreamhack CTF Season 4 Round #5, ๐ŸšฉDiv1 ์ถœ์ œ์ž‘) ๋ถ„๋ฅ˜: Pwnable (Browser / V8 JIT) ๋‚œ์ด๋„: ๐Ÿ’Ž Diamond 1 ์ด ๊ธ€: ์‹œ๋ฆฌ์ฆˆ 2ํŽธ โ€” ์†Œ์Šค ๋ถ€๊ฒ€. (1ํŽธ: ๋ฒ„๊ทธยทํŠธ๋ฆฌ๊ฑฐ์™€ ํ•˜๋“œ๋‹์˜ ๋ฒฝ)

1ํŽธ์—์„œ Math.exp ํ•œ ์ค„ ํŒจ์น˜๋กœ ํƒ€์ž…ํ˜ผ๋™์„ ํŠธ๋ฆฌ๊ฑฐํ–ˆ๊ณ , ์ž์—ฐ ํ‹ฐ์–ด์—…์—์„œ ๊ทธ ์–ด๊ธ‹๋‚จ์„ ๋ฐฐ์—ด ์ธ๋ฑ์Šค๊นŒ์ง€ ์‚ด๋ ค ๋ณด๋‚ด๋Š” ๋ฐ ์„ฑ๊ณตํ–ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ธ๋ฑ์Šค๊ฐ€ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋Š” ์ˆœ๊ฐ„, wild OOB๊ฐ€ ์•„๋‹ˆ๋ผ int3 ํŠธ๋žฉ(SIGTRAP) ์œผ๋กœ ๋Š๊ฒผ๋‹ค.

์ด 2ํŽธ์€ ๊ทธ ํŠธ๋žฉ์„ V8 11.2 ์†Œ์Šค๋กœ ๋ถ€๊ฒ€ํ•œ๋‹ค. ์™œ 11.2์—์„  typer๋ฅผ ์†์—ฌ CheckBounds๋ฅผ ์ง€์šฐ๋Š” ์ •์„(abiondoยทdoar-eยทQuick-Maffs)์ด ํ†ต์งธ๋กœ ๋ฌด๋ ฅํ™”๋˜๋Š”์ง€ โ€” ๊ทธ๊ฒŒ ๋‹จ์ˆœํžˆ "์–ด๋ ต๋‹ค"๊ฐ€ ์•„๋‹ˆ๋ผ ์†Œ์Šค์— ๋ฐ•ํžŒ ๋ฌด์กฐ๊ฑด ํ•˜๋“œ๋‹๊ณผ, doar-e์‹ ์šฐํšŒ๋ฅผ ์ฝ• ์ง‘์–ด ๋ง‰์€ ๋‘ ๋ฒˆ์งธ ๊ฒ€์‚ฌ ๋•Œ๋ฌธ์ž„์„ ์ค„ ๋ฒˆํ˜ธ์งธ๋กœ ์งš๋Š”๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ฐ™์€ ์‹œ๊ธฐ ํ•˜๋“œ๋‹์ด ์ผœ์ง„ V8์„ ์‹ค์ œ๋กœ ๋šซ์€ ์ต์Šค๋“ค์ด ์™œ ๊ฐ’-range typer ๋ฒ„๊ทธ๋ฅผ ๋ฒ„๋ฆฌ๊ณ  ๋‹ค๋ฅธ ๋ฒ„๊ทธ ํด๋ž˜์Šค๋กœ ๊ฐˆ์•„ํƒ”๋Š”์ง€ ๋Œ€์กฐํ•˜๋ฉฐ ๋๋‚ธ๋‹ค.

์†Œ์Šค๋Š” ์ฑŒ๋ฆฐ์ง€ patches/README.md๊ฐ€ ์ง€์ •ํ•œ ๋นŒ๋“œ ๊ธฐ์ค€ โ€” V8 commit 6538a20a (11.2.214.14). ์ธ์šฉํ•œ ์ค„ ๋ฒˆํ˜ธ๋Š” ๊ทธ commit์˜ ๊ฒƒ์ด๋‹ค.


๐Ÿ” 1ํŽธ์˜ ๋„๋‹ฌ์ 

1ํŽธ ๊ฒฐ๋ก ์„ ํ•œ ์ค„๋กœ: ๋ฒ„๊ทธยทํŠธ๋ฆฌ๊ฑฐ๋Š” ์‚ด์•„ ์žˆ๊ณ , ์–ด๊ธ‹๋‚จ์€ ์ธ๋ฑ์Šค๊นŒ์ง€ ๋„๋‹ฌํ•˜๋Š”๋ฐ, ๊ฑฐ๊ธฐ์„œ ํŠธ๋žฉ์ด๋‹ค.

ํ•ต์‹ฌ ๊ฐ€์ ฏ์€ ์ด๊ฑฐ์˜€๋‹ค โ€” b = Object.is(Math.exp(x), NaN)์€ typer์—” false(=0), ๋Ÿฐํƒ€์ž„์—” true(=1). ๋น„๊ต์™€ ๋ฐฐ์—ด ์ ‘๊ทผ ์‚ฌ์ด์— store๋ฅผ ๋ผ์›Œ b๋ฅผ ํด๋”ฉ ๋ชป ํ•˜๊ฒŒ ์‚ด๋ ค๋‘๋ฉด, a[b*K]์˜ ์ธ๋ฑ์Šค๊ฐ€ typer์—” 0, ๋Ÿฐํƒ€์ž„์—” K๋กœ ๊ฐˆ๋ฆฐ๋‹ค. ๋ฐฐ์—ด ๊ธธ์ด๋งŒ 8๊ณผ 4๋กœ ๋ฐ”๊ฟ” ๋Œ๋ฆฌ๋ฉด ํ•œ ํ™”๋ฉด์— ๋‹ค ๋‚˜์˜จ๋‹ค.

$ ./out/d8 --allow-natives-syntax trap_poc.js
len 8  : a[0](b=false)=1.1   trigger a[b*7] = 8.1   (๋Ÿฐํƒ€์ž„ ์ธ๋ฑ์Šค 7 โ†’ a[7]=8.1)
len 4  : a[0](b=false)=1.1   trigger โ†’
$ echo $?
133                                                  # 128 + 5 = SIGTRAP

len 8์€ ์ธ๋ฑ์Šค 7์ด ๊ธธ์ด 8 ์•ˆ์ด๋ผ a[7]=8.1์„ ๋ฌด์‚ฌํžˆ ๋Œ๋ ค์ค€๋‹ค โ€” ์–ด๊ธ‹๋‚จ์ด ๋Ÿฐํƒ€์ž„ ์ธ๋ฑ์Šค๊นŒ์ง€ ์‚ด์•„ ๋„๋‹ฌํ–ˆ๋‹ค๋Š” ์ง์ ‘ ์ฆ๊ฑฐ(typer๋Š” 0์ด๋ผ ๋ฏฟ์—ˆ๋‹ค). len 4๋Š” ๋˜‘๊ฐ™์€ ์ธ๋ฑ์Šค 7์ด ์ด๋ฒˆ์—” ๋ฒ”์œ„ ๋ฐ–์ด๊ณ , ๊ทธ ์ˆœ๊ฐ„ ์ฆ‰์‚ฌํ•œ๋‹ค. ๋ฐฐ์—ด ๊ธธ์ด ํ•˜๋‚˜๋งŒ ๋ฐ”๊ฟจ๋Š”๋ฐ ๊ฒฐ๊ณผ๊ฐ€ ๊ฐˆ๋ฆฌ๋‹ˆ, ํŠธ๋žฉ์˜ ์›์ธ์ด ๊ฒฝ๊ณ„๊ฒ€์‚ฌ๋ผ๋Š” ๊ฑด ๋ถ„๋ช…ํ•˜๋‹ค. ๋‚จ์€ ์งˆ๋ฌธ์€ ํ•˜๋‚˜ โ€” ์™œ wild read๊ฐ€ ์•„๋‹ˆ๋ผ ํŠธ๋žฉ์ธ๊ฐ€?



๐ŸŽฃ ๊ณ๊ฐ€์ง€ โ€” ํŒจ์น˜๋Š” ์–ด๋–ป๊ฒŒ ์•ˆ ํƒ€๋Š”๊ฐ€

๋ถ€๊ฒ€ ์ „์— ํ•˜๋‚˜ ์งš๊ณ  ๊ฐ€์ž. Math.exp ๋ฐ˜ํ™˜ํƒ€์ž…์„ PlainNumber๋กœ ๋ฐ”๊พผ ํŒจ์น˜๋Š” typer.cc์˜ JSCallTyper์— ์žˆ๋‹ค โ€” ์ฆ‰ Math.exp๊ฐ€ JSCall ๋…ธ๋“œ๋กœ ๋‚จ์•„ ์žˆ์„ ๋•Œ๋งŒ ํƒ„๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ํ‰๋ฒ”ํ•˜๊ฒŒ Math.exp(x)๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  hotํ•ด์ง€๋ฉด, JSCallReducer::ReduceMathUnary๊ฐ€ ๊ทธ๊ฑธ NumberExp๋ผ๋Š” ์ €์ˆ˜์ค€ ๋…ธ๋“œ๋กœ ๋‚ฎ์ถฐ๋ฒ„๋ฆฐ๋‹ค. NumberExp์˜ ํƒ€์ž…์€ OperationTyper๊ฐ€ ๋งค๊ธฐ๋Š”๋ฐ ๊ฑฐ๊ธด ํŒจ์น˜๊ฐ€ ์—†์–ด ์ •์งํ•˜๊ฒŒ NaN์„ ํฌํ•จํ•œ๋‹ค โ€” ๊ทธ๋ž˜์„œ ํŒจ์น˜๋œ typer๊ฐ€ ํƒ€์ง€๋ฅผ ์•Š๋Š”๋‹ค. ๋ฒ„๊ทธ๊ฐ€ ์žˆ๋Š”๋ฐ๋„ ํ‰๋ฒ”ํ•˜๊ฒŒ ์งœ๋ฉด ์žฌํ˜„์ด ์•ˆ ๋˜๋Š” ์ด์œ ๋‹ค.

ReduceMathUnary๋Š” ์ฝœ์‚ฌ์ดํŠธ speculation์ด ๊บผ์ ธ ์žˆ์œผ๋ฉด(kDisallowSpeculation) ๋‚ฎ์ถ”๊ธฐ๋ฅผ ํฌ๊ธฐํ•˜๊ณ  Math.exp๋ฅผ JSCall๋กœ ๋‘”๋‹ค. ๊ทธ์ œ์„œ์•ผ ํŒจ์น˜๋œ JSCallTyper โ†’ PlainNumber๊ฐ€ ์ ์šฉ๋ผ ๋ฒ„๊ทธ๊ฐ€ ๊นจ์–ด๋‚œ๋‹ค. speculation์€ ํ•œ ๋ฒˆ deopt๊ฐ€ ๋‚˜๋ฉด ๊ทธ ์ฝœ์‚ฌ์ดํŠธ์—์„œ ๊บผ์ง€๋ฏ€๋กœ โ€” ๋˜๋Š” ์ฒ˜์Œ๋ถ€ํ„ฐ ์ˆซ์ž ์•„๋‹Œ ์ธ์ž(๋ฌธ์ž์—ด)๋กœ ๋ถ€๋ฅด๋ฉด ์ˆซ์ž speculation์ด ์•ˆ ๊น”๋ฆฌ๋ฏ€๋กœ โ€” ๋‘˜ ์ค‘ ํ•˜๋‚˜๋กœ ๋„๋ฉด ๋œ๋‹ค. 1ํŽธ ํŠธ๋ฆฌ๊ฑฐ(f(NaN)=111)๊ฐ€ ๊ทธ "deopt๋กœ speculation off โ†’ JSCall ์œ ์ง€ โ†’ PlainNumber" ๊ฒฝ๋กœ๊ณ , ์„œ๋ฒ„์šฉ ์ž์—ฐ ํ‹ฐ์–ด์—… ๋ฒ„์ „์€ ๋ฌธ์ž์—ด ์ธ์ž(Math.exp("z")=NaN)๋กœ ๊ฐ™์€ ํšจ๊ณผ๋ฅผ ๋‚ธ๋‹ค. ์š”์ปจ๋Œ€ ์ด ๋ฒ„๊ทธ๋Š” ๊ธฐ๋ณธ ๊ฒฝ๋กœ์—์„  ์ž ๋“ค์–ด ์žˆ๊ณ , speculation์„ ๊บผ์•ผ ๋น„๋กœ์†Œ ๊นจ์–ด๋‚œ๋‹ค.


๐Ÿญ CheckBounds๊ฐ€ ๊ธฐ๊ณ„์–ด๊ฐ€ ๋˜๊ธฐ๊นŒ์ง€

arr[i]์˜ ๊ฒฝ๊ณ„๊ฒ€์‚ฌ๋Š” TurboFan ๊ทธ๋ž˜ํ”„์—์„œ CheckBounds ๋…ธ๋“œ ํ•˜๋‚˜๋กœ ์‹œ์ž‘ํ•ด, ์—ฌ๋Ÿฌ ๋‹จ๊ณ„๋ฅผ ๊ฑฐ์ณ ๊ธฐ๊ณ„์–ด๊ฐ€ ๋œ๋‹ค. ์šฐ๋ฆฌ ๋ถ€๊ฒ€์— ํ•„์š”ํ•œ ์„ธ ๋‹จ๊ณ„๋งŒ ์ถ”๋ฆฌ๋ฉด ์ด๋ ‡๋‹ค.

SimplifiedLowering         :  CheckBounds  โ†’  CheckedUint32Bounds(flags)
EffectControlLinearization :  CheckedUint32Bounds  โ†’  Uint32LessThan(idx, len)
                              โ†’ ๋น„๊ต ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ  LoadElement  ๋˜๋Š”  (deopt | Unreachable)

์—ฌ๊ธฐ์„œ ์šด๋ช…์„ ๊ฐ€๋ฅด๋Š” ๊ฑด CheckedUint32Bounds์— ๋ถ™๋Š” flags ๋‹ค. ๊ทธ ํ”Œ๋ž˜๊ทธ์— kAbortOnOutOfBounds๊ฐ€ ์žˆ๋А๋ƒ ์—†๋А๋ƒ๊ฐ€, ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚ฌ์„ ๋•Œ "์•ˆ์ „ํ•˜๊ฒŒ deopt"ํ• ์ง€ "๊ทธ ์ž๋ฆฌ์—์„œ abort(ํŠธ๋žฉ)"ํ• ์ง€๋ฅผ ์ •ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  1ํŽธ์—์„œ ๋ดค๋“ฏ ์˜› V8์—” ์…‹์งธ ์„ ํƒ์ง€ โ€” ๊ฒ€์‚ฌ๋ฅผ ํ†ต์งธ๋กœ ์ œ๊ฑฐ(= ๊ฒ€์‚ฌ ์—†๋Š” wild OOB) โ€” ๊ฐ€ ์žˆ์—ˆ๋‹ค. 11.2๋Š” ๊ทธ ์…‹์งธ ์„ ํƒ์ง€๋ฅผ ์—†์•ด๋‹ค.



๐Ÿงฑ ๋ฌด์กฐ๊ฑด abort โ€” simplified-lowering.cc

CheckBounds๋ฅผ ๋‚ฎ์ถ”๋Š” VisitCheckBounds์˜ ํ•ต์‹ฌ์€ ์ด ํ•œ ์กฐ๊ฐ์ด๋‹ค.

// src/compiler/simplified-lowering.cc โ€” VisitCheckBounds (์š”์•ฝ)
Type const index_type  = TypeOf(node->InputAt(0));
Type const length_type = TypeOf(node->InputAt(1));
...
if (lower<T>()) {
  if (index_type.IsNone() || length_type.IsNone() ||
      (index_type.Min() >= 0.0 &&
       index_type.Max() < length_type.Min())) {
    // The bounds check is redundant if we already know that
    // the index is within the bounds of [0.0, length[.
    new_flags |= CheckBoundsFlag::kAbortOnOutOfBounds;   // โ† ์ œ๊ฑฐ๊ฐ€ ์•„๋‹ˆ๋ผ abort
  }
  ChangeOp(node, simplified()->CheckedUint32Bounds(feedback, new_flags));
}

์ฝ๋Š” ๋ฒ•: typer๊ฐ€ index.Max() < length.Min() โ€” ์ฆ‰ "์ธ๋ฑ์Šค๋Š” ํ•ญ์ƒ ๊ธธ์ด๋ณด๋‹ค ์ž‘๋‹ค"๋ฅผ ์ฆ๋ช…ํ•˜๋ฉด, ์˜›๋‚ ์—” ์ด CheckBounds๋ฅผ ์ง€์› ๋‹ค(redundant๋‹ˆ๊นŒ). 11.2๋Š” ์ง€์šฐ๋Š” ๋Œ€์‹  kAbortOnOutOfBounds ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ถ™์ธ๋‹ค. ์ด๊ฒŒ ์ „๋ถ€๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ๋นŒ๋“œ(commit 6538a20a)์—” ์ด ๋™์ž‘์„ ๋„๋Š” ํ”Œ๋ž˜๊ทธ ์ž์ฒด๊ฐ€ ์—†๋‹ค โ€” ์ฝ”๋“œ์— ๋ฌด์กฐ๊ฑด ๋ฐ•ํ˜€ ์žˆ๋‹ค.

์šฐ๋ฆฌ ๊ฐ€์ ฏ์ด ์ •ํ™•ํžˆ ์ด ํ•จ์ •์— ๊ฑธ๋ฆฐ๋‹ค. ์ž์—ฐ ํ‹ฐ์–ด์—…์—์„œ b๋Š” ํ”ผ๋“œ๋ฐฑ์ƒ ํ•ญ์ƒ false๋ผ typer๊ฐ€ b*K๋ฅผ Range(0,0)์œผ๋กœ ์ขํžŒ๋‹ค โ†’ index.Max()=0 < length.Min()=4 โ†’ ํ”Œ๋ž˜๊ทธ ON. typer๋Š” "์ด ์ธ๋ฑ์Šค๋Š” ์ ˆ๋Œ€ 4 ๋ฏธ๋งŒ"์ด๋ผ ์ฆ๋ช…ํ–ˆ๊ณ , ๋Ÿฐํƒ€์ž„์ด ๊ทธ ์ฆ๋ช…์„ ๊นจ๋Š” ์ˆœ๊ฐ„์ด ๊ณง abort๋‹ค.

ํ•œ ๊ฐ€์ง€ ๊ณ๊ฐ€์ง€ โ€” ์œ„ ๋ถ„๊ธฐ๋Š” length_type.Is(Type::Unsigned31())์ผ ๋•Œ๋งŒ์ด๋‹ค(์ผ๋ฐ˜ JSArray๋Š” ๊ธธ์ด๊ฐ€ 2ยณยน ๋ฏธ๋งŒ์ด๋ผ ์—ฌ๊ธฐ ํ•ด๋‹น). length๊ฐ€ ๊ทธ๋ณด๋‹ค ํฐ ๊ฒฝ์šฐ(์˜ˆ: TypedArray, ๊ธธ์ด ์ตœ๋Œ€ 2โตยณ)๋Š” else ๊ฐ€์ง€๋กœ ๋น ์ ธ CheckedUint64Bounds๊ฐ€ ๋˜๋Š”๋ฐ, ๊ทธ์ชฝ์—” abort ํ”Œ๋ž˜๊ทธ๋ฅผ ์•ˆ ๋ถ™์ธ๋‹ค. ์ฆ‰ TypedArray OOB๋Š” ํŠธ๋žฉ์ด ์•„๋‹ˆ๋ผ deopt(์•ˆ์ „ ๋ณต๊ท€)๋‹ค. ์†”๊นƒํ•˜์ง€๋งŒ ๊ฒฐ๊ตญ wild๋Š” ์•„๋‹ˆ๋‹ค โ€” deopt๋„ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๋กœ ๋น ์ ธ undefined๋ฅผ ์ค„ ๋ฟ์ด๊ณ , ๊ฒŒ๋‹ค๊ฐ€ TypedArray๋Š” backing์ด ๋ถ„๋ฆฌ๋ผ ์žˆ์–ด OOB๊ฐ€ ๋‹ค๋ฅธ ๊ฐ์ฒด๋กœ ๋„˜์–ด๊ฐ€์ง€๋„ ์•Š๋Š”๋‹ค. ์–ด๋А ๋ถ„๊ธฐ๋“  "๊ฒ€์‚ฌ ์—†๋Š” ๋กœ๋“œ"๋Š” ์—†๋‹ค.


๐Ÿ’€ Unreachable์˜ ์ •์ฒด โ€” effect-control-linearizer.cc

๊ทธ abort๊ฐ€ ์‹ค์ œ๋กœ ๋ฌด์Šจ ๊ธฐ๊ณ„์–ด๊ฐ€ ๋˜๋Š”์ง€๋Š” ๋‹ค์Œ ๋‹จ๊ณ„์— ์žˆ๋‹ค.

// src/compiler/effect-control-linearizer.cc โ€” LowerCheckedUint32Bounds
Node* check = __ Uint32LessThan(index, limit);
if (!(params.flags() & CheckBoundsFlag::kAbortOnOutOfBounds)) {
  __ DeoptimizeIfNot(DeoptimizeReason::kOutOfBounds, ..., check, frame_state);  // ์•ˆ์ „ deopt
} else {
  auto if_abort = __ MakeDeferredLabel();
  auto done = __ MakeLabel();
  __ Branch(check, &done, &if_abort);
  __ Bind(&if_abort);
  __ Unreachable();          // โ† abort. ๋ฒ”์œ„ ๋ฐ–์ด๋ฉด ์—ฌ๊ธฐ๋กœ.
  __ Bind(&done);
}
return index;

ํ”Œ๋ž˜๊ทธ๊ฐ€ ์—†์œผ๋ฉด DeoptimizeIfNot โ€” ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋ฉด deopt(์ธํ„ฐํ”„๋ฆฌํ„ฐ๋กœ ์•ˆ์ „ ๋ณต๊ท€, undefined ๋ฐ˜ํ™˜). ํ”Œ๋ž˜๊ทธ๊ฐ€ ์žˆ์œผ๋ฉด Branch โ†’ Unreachable() โ€” ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋ฉด ์ฃฝ๋Š”๋‹ค. ์ด Unreachable์ด 1ํŽธ ๋””์Šค์–ด์…ˆ๋ธ”๋ฆฌ์—์„œ ๋ณธ jnc โ†’ call [r13+โ€ฆ] โ†’ int3๋‹ค. deopt๊ฐ€ ์•„๋‹ˆ๋ผ ํŠธ๋žฉ์ธ ์ด์œ ๊ฐ€ ์—ฌ๊ธฐ ์žˆ๋‹ค. typer๊ฐ€ "๋ถˆ๊ฐ€๋Šฅ"์ด๋ผ๊ณ  ์•ฝ์†ํ•œ ๋ถ„๊ธฐ๋ฅผ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋„๋‹ฌ ๋ถˆ๊ฐ€๋กœ ๊ตณํ˜€๋ฒ„๋ ธ์œผ๋‹ˆ, ๊ทธ ์•ฝ์†์ด ๊นจ์ง€๋ฉด ๊ฐˆ ๊ณณ์ด int3๋ฟ์ด๋‹ค.

์ •๋ฆฌํ•˜๋ฉด โ€” typer๊ฐ€ ์ธ๋ฑ์Šค๋ฅผ in-bounds๋กœ ์ฆ๋ช…ํ• ์ˆ˜๋ก ์ฝ”๋“œ๋Š” ๋” ์œ„ํ—˜ํ•ด์ง„๋‹ค. ์ฆ๋ช…์ด ๊ณง abort ๊ฐ€๋“œ๋ฅผ ๋ถ€๋ฅด๊ณ , ์šฐ๋ฆฌ ๋ฒ„๊ทธ๋Š” ๋ฐ”๋กœ ๊ทธ ์ฆ๋ช…์„ ๊ฑฐ์ง“์œผ๋กœ ๋งŒ๋“ ๋‹ค.


๐Ÿ˜– ์•ฝ ์˜ฌ๋ฆฌ๋Š” ์ง€์  โ€” ์ž์—ฐ ํ‹ฐ์–ด์—…์€ abiondo์˜ ์กฐ๊ฑด์„ ๋‹ค ๋งŒ์กฑํ•œ๋‹ค

์—ฌ๊ธฐ๊ฐ€ ์ด ๋ฌธ์ œ์—์„œ ๊ฐ€์žฅ ์•ฝ ์˜ค๋ฅด๋Š” ๋ถ€๋ถ„์ด๋‹ค. ์ž์—ฐ ํ‹ฐ์–ด์—…์œผ๋กœ ์ปดํŒŒ์ผํ•œ ๊ฐ€์ ฏ์„ ๋””์Šค์–ด์…ˆ๋ธ”ํ•ด๋ณด๋ฉด, abiondo๊ฐ€ OOB๋ฅผ ์–ป์œผ๋ ค๊ณ  ํ•„์š”๋กœ ํ–ˆ๋˜ ์กฐ๊ฑด์„ ์ด ๋นŒ๋“œ๊ฐ€ ๋‹ค ๋งŒ๋“ค์–ด์ค€๋‹ค. ๊ทธ๋Ÿฐ๋ฐ๋„ ํŠธ๋žฉ์ด๋‹ค.

$ ./out/d8 --allow-natives-syntax --print-opt-code dis.js
...
0x..  setzl al                     ; b = Object.is(...)  โ† ๋Ÿฐํƒ€์ž„์— *๊ณ„์‚ฐ*๋œ๋‹ค (์ƒ์ˆ˜ ์•„๋‹˜)
0x..  imull rcx, rax, 0x14         ; rcx = b * 20        โ† ๊ณฑ์…ˆ ๋…ธ๋“œ๊ฐ€ ์‚ด์•„์žˆ๋‹ค
0x..  cmpl  [rbp-0x28], 0x4        ; idx vs length(4)
0x..  jnc   <+0x331>               ; ๋ฒ”์œ„ ๋ฐ–์ด๋ฉด โ†’ abort ์‹œํ€€์Šค(call โ†’ int3)
0x..  vmovsd xmm1,[r8+rcx*8+0x7]   ; a[idx] ๋กœ๋“œ

๋ชจ์ˆœ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค โ€” b๊ฐ€ setzl๋กœ ๋Ÿฐํƒ€์ž„์— ๊ณ„์‚ฐ๋˜๊ณ (์ƒ์ˆ˜ 0์œผ๋กœ ํด๋”ฉ๋์œผ๋ฉด ์ด ๋ช…๋ น ์ž์ฒด๊ฐ€ ์—†๋‹ค), b*20๋„ imull๋กœ ์‚ด์•„์žˆ๋‹ค(๊ณฑ์…ˆ ๋…ธ๋“œ ์ƒ์กด). ์ฆ‰ ๋ฒฝ 1์„ ํ†ต๊ณผํ–ˆ๋‹ค: ์–ด๊ธ‹๋‚จ์ด ์ฃฝ์ง€ ์•Š๊ณ  ์ธ๋ฑ์Šค๊นŒ์ง€ ํ๋ฅธ๋‹ค. ๋™์‹œ์— cmpl idx, 0x4์— abort๊ฐ€ ๋ถ™์—ˆ๋‹ค๋Š” ๊ฑด, typer๊ฐ€ idx๋ฅผ Range(0,0)(= ํ•ญ์ƒ < 4)์œผ๋กœ ์ขํ˜”๋‹ค๋Š” ๋œป์ด๋‹ค.

์ด๊ฒŒ ์ •ํ™•ํžˆ abiondo๊ฐ€ ์›ํ–ˆ๋˜ ๊ทธ๋ฆผ์ด๋‹ค โ€” "๋Ÿฐํƒ€์ž„์—” ๊ณ„์‚ฐ๋˜๋Š”๋ฐ typer๋Š” ์ž‘๋‹ค๊ณ  ๋ฏฟ๋Š” ์ธ๋ฑ์Šค". ์˜› V8์ด์—ˆ๋‹ค๋ฉด ๊ทธ "typer๊ฐ€ ์ž‘๋‹ค๊ณ  ๋ฏฟ์Œ"์ด ๊ณง CheckBounds ์ œ๊ฑฐ์˜€๊ณ , ๋Ÿฐํƒ€์ž„ b=1 โ†’ idx=20์ด ๊ฒ€์‚ฌ ์—†์ด ํ†ต๊ณผํ•ด wild OOB๊ฐ€ ๋์„ ๊ฒƒ์ด๋‹ค. 11.2๋Š” ๊ทธ "๋ฏฟ์Œ"์„ ๊ฒ€์‚ฌ ์ œ๊ฑฐ๊ฐ€ ์•„๋‹ˆ๋ผ abort ๊ฐ€๋“œ๋กœ ๋ฐ”๊ฟจ๋‹ค. ๊ทธ๋ž˜์„œ ๊ฐ™์€ ๊ทธ๋ฆผ์ด wild OOB ๋Œ€์‹  ์ฆ‰์‚ฌ๋‹ค.

์•ฝ ์˜ค๋ฅด๋Š” ๊ฑด โ€” ์–ด๊ธ‹๋‚จ์„ ์ธ๋ฑ์Šค๊นŒ์ง€ ์‚ด๋ ค๋ณด๋‚ด๋Š” ์–ด๋ ค์šด ๋ถ€๋ถ„์€ ๋‹ค ๋๋Š”๋ฐ, ๋งˆ์ง€๋ง‰ ํ•œ ์นธ(๊ฒ€์‚ฌ ์ œ๊ฑฐ โ†” abort)์—์„œ ๋ง‰ํžŒ๋‹ค๋Š” ์ ์ด๋‹ค. 1ํŽธ์—์„œ ๊ฐ์ฒด ํ•„๋“œ ์„ผํ‹ฐ๋„ฌ์ด Boolean์œผ๋กœ ๋‚จ์•„ ๋ชป ์ขํ˜€์กŒ๋˜ ๊ฒƒ๊ณผ ๋‹ฌ๋ฆฌ, ์ž์—ฐ ํ‹ฐ์–ด์—…์€ ํ”ผ๋“œ๋ฐฑ("b๋Š” ํ•ญ์ƒ false")์œผ๋กœ b๋ฅผ Range(0,0)๊นŒ์ง€ ์ขํžˆ๋ฉด์„œ๋„ ๋…ธ๋“œ๋ฅผ ์‚ด๋ ค๋‘”๋‹ค. abiondoยทdoar-e ์‹œ๋Œ€์˜€๋‹ค๋ฉด ์—ฌ๊ธฐ์„œ ๊ฒŒ์ž„์ด ๋๋‚ฌ๋‹ค. 11.2์—์„  ์—ฌ๊ธฐ๊ฐ€ ์‹œ์ž‘์ด๊ณ  ๋™์‹œ์— ๋์ด๋‹ค.

turbolizer๋กœ ์ง์ ‘ ํ™•์ธ

๊ฐ™์€ ์–ด๊ธ‹๋‚จ ํŒจํ„ด์„ ๋‹ด์€ ๋” ์งง์€ ๋ณ€ํ˜•(dis.js, a[b*20] ๋Œ€์‹  a[b*5])์„ --trace-turbo๋กœ ๋ฝ‘์•„ turbolizer์— ์˜ฌ๋ ค๋ดค๋‹ค. effect-linearization ๋‹จ๊ณ„์˜ sea-of-nodes ๊ทธ๋ž˜ํ”„๋‹ค.

turbolizer โ€” dis.js foo() ์˜ effect-linearization ๋‹จ๊ณ„ sea-of-nodes ๊ทธ๋ž˜ํ”„
turbolizer โ€” dis.js foo() ์˜ effect-linearization ๋‹จ๊ณ„ sea-of-nodes ๊ทธ๋ž˜ํ”„

๊ทธ๋ž˜ํ”„๋งŒ์œผ๋ก  CheckBounds ๋…ธ๋“œ ํ•˜๋‚˜๋ฅผ ๋ˆˆ์œผ๋กœ ์งš๊ธฐ ์–ด๋ ค์›Œ์„œ, ๊ฐ™์€ ์ปดํŒŒ์ผ์„ --trace-turbo-reduction์œผ๋กœ๋„ ๋ฝ‘์•„ ๊ฐ ๋ฆฌ๋“€์„œ๊ฐ€ CheckBounds๋ฅผ ์–ด๋–ป๊ฒŒ ๊ฑด๋“œ๋ฆฌ๋Š”์ง€ ํ…์ŠคํŠธ๋กœ ๋Œ€์กฐํ–ˆ๋‹ค.

./out/d8 --allow-natives-syntax --trace-turbo-reduction dis.js 2>&1 | grep -iE 'CheckBounds' | head -6
--trace-turbo-reduction โ€” CheckBounds ๋…ธ๋“œ๊ฐ€ Typerโ†’TypedOptimizationโ†’RedundancyEliminationโ†’LoadElimination ์ˆœ์„œ๋กœ ๊ฐฑ์‹ ๋˜๋Š” ์‹ค์ œ ๋กœ๊ทธ
--trace-turbo-reduction โ€” CheckBounds ๋…ธ๋“œ๊ฐ€ Typerโ†’TypedOptimizationโ†’RedundancyEliminationโ†’LoadElimination ์ˆœ์„œ๋กœ ๊ฐฑ์‹ ๋˜๋Š” ์‹ค์ œ ๋กœ๊ทธ

ํ•ต์‹ฌ์€ #110: CheckBounds[...]๊ฐ€ ์—ฌ๋Ÿฌ ๋ฆฌ๋“€์„œ(Typerโ†’TypedOptimizationโ†’RedundancyEliminationโ†’LoadElimination)๋ฅผ ๊ฑฐ์น˜๋Š” ๋™์•ˆ ์ง€์›Œ์ง€์ง€ ์•Š๊ณ  ๊ณ„์† in-place update๋งŒ ๋œ๋‹ค๋Š” ์ ์ด๋‹ค โ€” ๋…ธ๋“œ ์ž์ฒด๊ฐ€ ํŒŒ์ดํ”„๋ผ์ธ ๋๊นŒ์ง€ ์‚ด์•„๋‚จ๋Š”๋‹ค. ์œ„์—์„œ ๋””์Šค์–ด์…ˆ๋ธ”๋ฆฌ๋กœ ๋ณธ "๊ฒ€์‚ฌ ์ž์ฒด๋Š” ์•ˆ ์ง€์›Œ์ง„๋‹ค"๋Š” ์ฃผ์žฅ์ด ์ปดํŒŒ์ผ ํŒŒ์ดํ”„๋ผ์ธ ๋กœ๊ทธ๋กœ๋„ ๊ทธ๋Œ€๋กœ ํ™•์ธ๋œ๋‹ค.


๐Ÿชฆ doar-e ์šฐํšŒ์™€ ๊ทธ ๋ฌด๋ค โ€” js-native-context-specialization.cc

์—ฌ๊ธฐ๊นŒ์ง€๋Š” "ํ‘œ์ค€ ๊ฒฝ๊ณ„๊ฒ€์‚ฌ" ์ด์•ผ๊ธฐ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ doar-e๊ฐ€ 2019๋…„์— ๋ณด์—ฌ์ค€ ์šฐํšŒ๊ฐ€ ์žˆ๋‹ค โ€” OOB-load-mode. ๋ฐฐ์—ด ์ ‘๊ทผ์ด ์ธํ„ฐํ”„๋ฆฌํ„ฐ ๋‹จ๊ณ„์—์„œ ๋ฒ”์œ„ ๋ฐ– ์ธ๋ฑ์Šค๋ฅผ ๋ณธ ์ ์ด ์žˆ์œผ๋ฉด, IC๊ฐ€ ๊ทธ ์Šฌ๋กฏ์„ "OOB ํ—ˆ์šฉ ๋กœ๋“œ"๋กœ ๊ธฐ๋กํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด TurboFan์€ abortํ•˜๋Š” CheckBounds ๋Œ€์‹  NumberLessThan(idx, len) + (์‹คํŒจ ์‹œ undefined) ํŒจํ„ด์œผ๋กœ ์ปดํŒŒ์ผํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด NumberLessThan์€ ์ˆœ์ˆ˜ ๋น„๊ต ๋…ธ๋“œ๋ผ, typer๊ฐ€ idx < len์„ ์ฆ๋ช…ํ•˜๋ฉด ConstantFoldingReducer๊ฐ€ ํ†ต์งธ๋กœ ์ง€์›Œ๋ฒ„๋ฆฐ๋‹ค โ€” abort ๊ฐ€๋“œ ์—†์ด. doar-e๋Š” ๊ฑฐ๊ธฐ์„œ ๊ฒ€์‚ฌ ์—†๋Š” OOB๋ฅผ ์–ป์—ˆ๋‹ค.

11.2์˜ element-access ๋นŒ๋” ์†Œ์Šค๋ฅผ ์—ด์–ด๋ณด๋ฉด, ๊ทธ NumberLessThan ์•ˆ์ชฝ์— ๋‘ ๋ฒˆ์งธ CheckBounds๊ฐ€ ๋ฐ•ํ˜€ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ฃผ์„์ด ์˜๋„๋ฅผ ํ† ์”จ ํ•˜๋‚˜ ์•ˆ ์ˆจ๊ธฐ๊ณ  ์ ์–ด๋†จ๋‹ค.

// src/compiler/js-native-context-specialization.cc โ€” LOAD_IGNORE_OUT_OF_BOUNDS
Node* check = graph()->NewNode(simplified()->NumberLessThan(), index, length);
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
{
  // Do a real bounds check against {length}. This is in order to
  // protect against a potential typer bug leading to the elimination of
  // the NumberLessThan above.
  index = etrue = graph()->NewNode(
      simplified()->CheckBounds(
          FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero |
                                CheckBoundsFlag::kAbortOnOutOfBounds),   // โ† ๋˜ abort
      index, length, etrue, if_true);
  vtrue = etrue = graph()->NewNode(simplified()->LoadElement(element_access), ...);
}

NumberLessThan์„ typer๋กœ ์ง€์›Œ๋„, if_true ๋ถ„๊ธฐ ์•ˆ์—์„œ kAbortOnOutOfBounds๊ฐ€ ๋ถ™์€ ๋‘ ๋ฒˆ์งธ CheckBounds ๊ฐ€ ๋˜ ๋ง‰๋Š”๋‹ค. ์ฃผ์„์ด ๊ทธ๋Œ€๋กœ โ€” "protect against a potential typer bug leading to the elimination of the NumberLessThan above" โ€” "NumberLessThan์„ ์ง€์šฐ๋Š” typer ๋ฒ„๊ทธ"๋ฅผ ์ฝ• ์ง‘์–ด ๋ฐฉ์–ดํ•œ๋‹ค๊ณ  ์„ ์–ธํ•œ๋‹ค. doar-e์‹ ์šฐํšŒ๋ฅผ ์ •์กฐ์ค€ํ•œ ๋ฌด๋ค์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ๋˜‘๊ฐ™์€ ์ฃผ์„+ํŒจํ„ด์ด jnscs.cc์— ๋„ค ๋ฒˆ(load 3243, store/grow 3662ยท3742ยท3822) ๋ฐ•ํ˜€ ์žˆ๋‹ค โ€” load๋“  store๋“  grow๋“ , OOB ํ—ˆ์šฉ ๋ชจ๋“œ๋ฅผ ์“ฐ๋Š” ๋ชจ๋“  element-access ๊ฒฝ๋กœ์— ๊ฐ™์€ ๋ฐฉ์–ด๋ฅผ ๊น”์•„๋‘” ๊ฒƒ์ด๋‹ค.

๊ฒŒ๋‹ค๊ฐ€ ์šฐ๋ฆฌ ๋ฒ„๊ทธ๋Š” ์ด ์šฐํšŒ๋ฅผ ์‹œ๋„ํ•  ์ž๊ฒฉ์กฐ์ฐจ ๋ชป ๊ฐ–์ถ˜๋‹ค. OOB-load-mode๋ฅผ ์ผœ๋ ค๋ฉด ํ›ˆ๋ จ ์ค‘ ๋ฒ”์œ„ ๋ฐ– ์ ‘๊ทผ์„ ๋ด์•ผ ํ•˜๊ณ (= b=true ํ˜ธ์ถœ ํ•„์š”), ๊ทธ๋Ÿฌ๋ฉด Object.is ํ”ผ๋“œ๋ฐฑ์ด Boolean์ด ๋ผ ์ธ๋ฑ์Šค๊ฐ€ Range(0,K)๋กœ ๋„“์–ด์ง„๋‹ค โ†’ NumberLessThan์ด ํด๋”ฉ ์•ˆ ๋จ. ๋ฐ˜๋Œ€๋กœ b=false๋งŒ ์ฃผ๋ฉด OOB-mode๊ฐ€ ์•ˆ ์ผœ์ ธ ์ฒซ ๋ฒˆ์งธ CheckBounds[abort]. ๋‘˜์ด ์ƒํ˜ธ๋ฐฐํƒ€๋‹ค. doar-e์˜ lastIndexOf ๋ฒ„๊ทธ๋Š” ๊ฒฐ๊ณผ๋ฅผ ์ •์ ์œผ๋กœ ์ž‘์€ ๋ฒ”์œ„๋กœ ์˜คํƒ€์ž…ํ•ด์„œ ํ”ผ๋“œ๋ฐฑ๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ Range(0,0)์ด ๋‚˜์™”์ง€๋งŒ, Math.exp๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋„“์€ PlainNumber๋กœ ์˜คํƒ€์ž…ํ•  ๋ฟ์ด๋ผ ์ธ๋ฑ์Šค๋ฅผ ์ž‘๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒŒ ํ”ผ๋“œ๋ฐฑ ์ถ”์ธก์— ์˜์กดํ•˜๊ณ , ๊ทธ๊ฒŒ OOB-mode์™€ ์ถฉ๋Œํ•œ๋‹ค.

V8 11.2 ์†Œ์Šค(jnscs.cc) โ€” doar-e์‹ NumberLessThan ํด๋”ฉ ์šฐํšŒ๋ฅผ ์ฝ• ์ง‘์–ด ๋ง‰์€ ๋‘ ๋ฒˆ์งธ CheckBounds[kAbortOnOutOfBounds]. loadยทstoreยทgrow ๊ฒฝ๋กœ๋งˆ๋‹ค ๊ฐ™์€ ์ฃผ์„+ํŒจํ„ด์ด ๋ฐ•ํ˜€ ์žˆ๋‹ค
V8 11.2 ์†Œ์Šค(jnscs.cc) โ€” doar-e์‹ NumberLessThan ํด๋”ฉ ์šฐํšŒ๋ฅผ ์ฝ• ์ง‘์–ด ๋ง‰์€ ๋‘ ๋ฒˆ์งธ CheckBounds[kAbortOnOutOfBounds]. loadยทstoreยทgrow ๊ฒฝ๋กœ๋งˆ๋‹ค ๊ฐ™์€ ์ฃผ์„+ํŒจํ„ด์ด ๋ฐ•ํ˜€ ์žˆ๋‹ค


๐Ÿšช ๋ชจ๋“  ์ถœ๊ตฌ๊ฐ€ ๋ง‰ํžŒ ์ด์œ 

"์ธ๋ฑ์Šค๊ฐ€ ์•ˆ ๋˜๋ฉด ๋‹ค๋ฅธ sink๋Š”?" ์‹ถ์–ด ๊ฐ€๋Šฅํ•œ ์ถœ๊ตฌ๋ฅผ ํ•˜๋‚˜์”ฉ ๋‹ค ๋‘๋“œ๋ ค๋ดค๋‹ค. ๊ฐ™์€ ์–ด๊ธ‹๋‚จ(typer๋Š” ์ž‘๋‹ค ๋ฏฟ๊ณ  ๋Ÿฐํƒ€์ž„์€ ํผ/NaN)์„ ์–ด๋””์— ๊ฝ‚์•„๋„ ๋ง‰ํžŒ๋‹ค. ๊ฐ ์ถœ๊ตฌ๊ฐ€ ์–ด๋А ์ฝ”๋“œ์— ๋ง‰ํžˆ๋Š”์ง€๊นŒ์ง€ ์ ๋Š”๋‹ค.

์ถœ๊ตฌ๊ฒฐ๊ณผ๋ง‰์€ ๋ฉ”์ปค๋‹ˆ์ฆ˜ (์†Œ์Šค)
a[b*K] ์ธ๋ฑ์Šค ๋กœ๋“œ/์Šคํ† ์–ดSIGTRAPkAbortOnOutOfBounds (simplified-lowering.cc)
OOB-load-mode + b*Kundefined / ํŠธ๋žฉ์•ˆ์ชฝ ๋‘ ๋ฒˆ์งธ CheckBounds[abort] (jnscs.cc)
Array(b*K) ๊ธธ์ด ๊ตฌ์„ฑ์‹ค์ œ 4096์นธ ํ• ๋‹นb*K๊ฐ€ ์ƒ์ˆ˜ ๋…ธ๋“œ ์•„๋‹˜ โ†’ ๋Ÿฐํƒ€์ž„๊ฐ’์œผ๋กœ ์ •์ƒ ์ƒ์„ฑ
arr.length = b*Kbacking ์‹ค์ œ grow(FixedDoubleArray[4096])๋Ÿฐํƒ€์ž„๊ฐ’์œผ๋กœ grow, ๊ธธ์ด/์šฉ๋Ÿ‰ ๋ถˆ์ผ์น˜ ์—†์Œ
store-and-grow a[b*K]=vSIGTRAPMaybeGrowFastElements ํ•˜๋“œ๋‹
t*0 โ†’ cvttsd2si(NaN)=INT_MINundefinedCheckedUint64Bounds (์Œ์ˆ˜โ†’๊ฑธ๋Ÿฌ์ง)
Math.max(0,t)/Math.min(t,5)divergence ์ง„์งœ์ง€๋งŒ ๋Ÿฐํƒ€์ž„โˆˆtyper๋ฒ”์œ„OOB/length๋กœ ๋ชป ํ‚ค์›€
๋ฃจํ”„ induction var i+=Math.expi = NaNNaN sticky โ€” ํฐ ์œ ํ•œ ์ธ๋ฑ์Šค๊ฐ€ ์•ˆ ๋‚˜์˜ด

๋งˆ์ง€๋ง‰ ์ค„์ด ํฅ๋ฏธ๋กญ๋‹ค. Project Zero์˜ "Chrome Infinity Bug"๋Š” induction ๋ณ€์ˆ˜๊ฐ€ -Inf + Inf๋กœ ํฐ ์œ ํ•œ๊ฐ’์ด ๋˜๋ฉฐ typer ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜ OOB๊ฐ€ ๋๋‹ค. ๊ฐ™์€ ํŠธ๋ฆญ์„ Math.exp(NaN)์œผ๋กœ ์‹œ๋„ํ•˜๋ฉด โ€” NaN์€ ์‚ฐ์ˆ ์—์„œ NaN์œผ๋กœ ์ „ํŒŒ๋˜๋‹ˆ โ€” induction ๋ณ€์ˆ˜๊ฐ€ ๊ทธ๋ƒฅ NaN์ด ๋œ๋‹ค.

$ ./out/d8 --allow-natives-syntax induction.js
for(i<1;i+=exp) ret i        => NaN        # i += NaN โ†’ NaN, ๋ฃจํ”„ ์ฆ‰์‹œ ํƒˆ์ถœ
while(i<exp)i++ ret i         => 0          # NaN > 0 false โ†’ ๋ฃจํ”„ 0ํšŒ
arr[ind] OOB?                => undefined   # NaN ์ธ๋ฑ์Šค โ†’ ์ด๋ฆ„ ์กฐํšŒ
Array(ind*4096)              => RangeError  # Array(NaN) ์˜ˆ์™ธ

์ด๊ฒŒ ์ด ๋ฒ„๊ทธ์˜ ๋ณธ์งˆ์  ํ•œ๊ณ„๋‹ค โ€” NaN์€ ๋ˆ์ ํ•˜๋‹ค. ์‚ฐ์ˆ ์— ๋„ฃ์œผ๋ฉด NaN์œผ๋กœ ์ „ํŒŒ๋˜๊ณ (โ†’ ToInt32=0 / Array=์˜ˆ์™ธ), ๋น„๊ต์— ๋„ฃ์œผ๋ฉด ๋ถˆ๋ฆฌ์–ธ์ด ๋˜๋Š”๋ฐ ๊ทธ๊ฑด ํด๋”ฉ๋˜๊ฑฐ๋‚˜(์ƒ์ˆ˜) ์ธ๋ฑ์Šค๋กœ ์“ฐ๋ฉด ํŠธ๋žฉ์ด๋‹ค. ํฐ ์œ ํ•œ ์ •์ˆ˜ ์ธ๋ฑ์Šค๋กœ ์ž๋ผ๋Š” ๊ธธ์ด ์—†๋‹ค.

induction-variable ๊ฒฝ๋กœ๋„ ๋ง‰ํž˜ โ€” Math.exp(NaN)์€ NaN sticky๋ผ Chrome Infinity Bug์‹ ํฐ ์œ ํ•œ๊ฐ’์ด ์•ˆ ๋‚˜์˜จ๋‹ค
induction-variable ๊ฒฝ๋กœ๋„ ๋ง‰ํž˜ โ€” Math.exp(NaN)์€ NaN sticky๋ผ Chrome Infinity Bug์‹ ํฐ ์œ ํ•œ๊ฐ’์ด ์•ˆ ๋‚˜์˜จ๋‹ค
โ–ถ๐Ÿ”ฌ ์ถœ๊ตฌ๋ฅผ ํ•˜๋‚˜์”ฉ ๋‘๋“œ๋ฆฐ ๊ธฐ๋ก (์‹ค์ธก ๋กœ๊ทธ)

ํ‘œ์˜ ๊ฐ ์ค„์€ ์ถ”์ธก์ด ์•„๋‹ˆ๋ผ ๋กœ์ปฌ d8 ์ธก์ •์ด๋‹ค. ๋ง‰ํžŒ ๋ฐฉ์‹์ด ์ถœ๊ตฌ๋งˆ๋‹ค ๋ฏธ๋ฌ˜ํ•˜๊ฒŒ ๋‹ฌ๋ผ์„œ, ๊ทธ ์ฐจ์ด๊ฐ€ ๊ฒฐ๊ตญ "๊ฐ’-range๋Š” ์–ด๋””๋กœ๋„ ์•ˆ ์ž๋ž€๋‹ค"๋ฅผ ๊ฐ€๋ฆฌ์ผฐ๋‹ค.

Array(b*K) โ€” ๋ถˆ์ผ์น˜ ์—†์Œ. typer๊ฐ€ b*K๋ฅผ Range(0,0)์œผ๋กœ ๋ฏฟ์œผ๋‹ˆ size-0 ํ• ๋‹น + ๋Ÿฐํƒ€์ž„ length๋กœ ๋ถˆ์ผ์น˜(Bug-1051017์‹)๋ฅผ ๋…ธ๋ ธ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ b*K๋Š” ์ƒ์ˆ˜ ๋…ธ๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ ๊ณ„์‚ฐ ๋…ธ๋“œ๋ผ, JSCreateArray๊ฐ€ ์ธ๋ผ์ธ ๊ณ ์ • ํ• ๋‹น์„ ์•ˆ ํ•˜๊ณ  ๋Ÿฐํƒ€์ž„๊ฐ’์œผ๋กœ ์ƒ์„ฑ์ž๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

$ ./out/d8 --allow-natives-syntax len_live.js
Array(b*4096).length        [TF] => len=4096
Array(b*4096) a[500] oob?    [TF] => len=4096 a[500]=undef   # ์ง„์งœ 4096์นธ, ๋ถˆ์ผ์น˜ X

arr.length = b*K โ€” backing์ด ์‹ค์ œ๋กœ ํฐ๋‹ค. typer๊ฐ€ length-set์„ "0์œผ๋กœ ์ค„์ž„(shrink)"์œผ๋กœ ๋ฏฟ์–ด grow๋ฅผ ์ƒ๋žตํ•˜๊ธธ(โ†’ length๋งŒ ๊ฑฐ๋Œ€, capacity ์ž‘์Œ) ๊ธฐ๋Œ€ํ–ˆ๋‹ค. %DebugPrint๋กœ ๋ณด๋ฉด backing์ด ๊ทธ๋ƒฅ 4096์นธ์œผ๋กœ ์ž๋ž€๋‹ค.

$ ./out/d8 --allow-natives-syntax shrink_probe.js
a.length = 4096
DebugPrint: [JSArray]
 - elements: <FixedDoubleArray[4096]> [HOLEY_DOUBLE_ELEMENTS]   # capacity ๋„ 4096 โ€” ๋ถˆ์ผ์น˜ X

store-and-grow a[b*K]=v โ€” ํŠธ๋žฉ. MaybeGrowFastElements ๊ฒฝ๋กœ๋„ ํ•˜๋“œ๋‹๋ผ ์žˆ์–ด, ๋นˆ ๋ฐฐ์—ด์—์„œ grow ํ›ˆ๋ จ ํ›„ ํŠธ๋ฆฌ๊ฑฐํ•˜๋ฉด SIGTRAP(exit 133).

t*0 โ†’ cvttsd2si(NaN) = INT_MIN. floatโ†’int ๋ณ€ํ™˜์—์„œ NaN ๋ณด์ • ์—†๋Š” vcvttsd2si๊ฐ€ emit๋ผ INT_MIN(๊ฑฐ๋Œ€ ์Œ์ˆ˜)์ด ๋‚˜์˜ค์ง€๋งŒ, CheckedUint64Bounds๊ฐ€ unsigned๋กœ ๊ฑธ๋Ÿฌ undefined.

Math.max/min โ€” ์–ด๊ธ‹๋‚จ์€ ์ง„์งœ์ธ๋ฐ ๋ฌด์šฉ. typer๊ฐ€ no-NaN์„ ๋ฏฟ์–ด maxsd/minsd์˜ NaN ๋ณด์ •์„ ์ƒ๋žตํ•˜๋ฏ€๋กœ, Math.max(0,t)๊ฐ€ ์ŠคํŽ™(NaN)๊ณผ ๋‹ฌ๋ฆฌ 0์„ ๋Œ๋ ค์ค€๋‹ค(์ง„์งœ miscompile). ํ•˜์ง€๋งŒ ๋Ÿฐํƒ€์ž„ ๊ฐ’์ด ํ•ญ์ƒ typer ๋ฒ”์œ„ ์•ˆ์ด๋ผ(0 โˆˆ [0,โˆž), 5 โˆˆ (-โˆž,5]) OOB๋กœ๋„ length๋กœ๋„ ๋ชป ํ‚ค์šด๋‹ค.

$ ./out/d8 --allow-natives-syntax probe_minmax.js
Math.max(0,t)   OPT=0     SPEC=NaN    DIVERGE    # maxsd(0,NaN)=0, ๊ทธ๋Ÿฌ๋‚˜ 0โˆˆ[0,โˆž)
Math.min(t,5)   OPT=5     SPEC=NaN    DIVERGE    # minsd(NaN,5)=5, ๊ทธ๋Ÿฌ๋‚˜ 5โˆˆ(-โˆž,5]

์—ฌ๋Ÿ ์ถœ๊ตฌ์˜ ๊ณตํ†ต ๊ฒฐ๋ก  โ€” ์–ด๊ธ‹๋‚จ์ด ๊ฐ’์œผ๋กœ ์“ฐ์ด๋ฉด ํด๋”ฉ๋˜๊ฑฐ๋‚˜(์ƒ์ˆ˜) ๋Ÿฐํƒ€์ž„์ด typer ๋ฒ”์œ„ ์•ˆ์ด๊ณ , ์ธ๋ฑ์Šค๋กœ ์“ฐ์ด๋ฉด abort๋‹ค. ๊ทธ ์‚ฌ์ด๊ฐ€ ์—†๋‹ค.



๐Ÿงฉ ๊ทผ๋ณธ ์›์ธ โ€” ๋‘ ๋ฐฉํ–ฅ์˜ ๋ฒฝ

์™œ ์ด๋ ‡๊ฒŒ ํ•œ ๊ณณ๋„ ์•ˆ ๋šซ๋ฆฌ๋Š”์ง€, ๋‘ ๋ฒฝ์ด ์ง๊ตํ•œ๋‹ค.

๋ฒฝ 1 โ€” fold โ†” live. ์–ด๊ธ‹๋‚จ์ด ์ต์Šค์— ์“ธ๋ชจ ์žˆ์œผ๋ ค๋ฉด โ‘  ์ปดํŒŒ์ผ ํƒ€์ž„์— ์ƒ์ˆ˜๋กœ ํด๋”ฉ๋˜๋ฉด ์•ˆ ๋˜๊ณ (ํด๋”ฉ๋˜๋ฉด typer ๊ฐ’ = ๋Ÿฐํƒ€์ž„ ๊ฐ’, self-consistent), โ‘ก ๊ทธ๋Ÿฌ๋ฉด์„œ typer๊ฐ€ ๊ทธ ํƒ€์ž…์„ ์ข๊ฒŒ ๋ฏฟ์–ด์ค˜์•ผ ํ•œ๋‹ค(๊ฒ€์‚ฌ ์ œ๊ฑฐ์˜ ๊ทผ๊ฑฐ). ๋ฆฌํ„ฐ๋Ÿด NaN ์„ผํ‹ฐ๋„ฌ์€ โ‘ ์„ ์–ด๊ฒจ ์ฆ‰์‹œ ํด๋”ฉ๋˜๊ณ , ๊ฐ์ฒด ํ•„๋“œ ์„ผํ‹ฐ๋„ฌ์€ โ‘ก๋ฅผ ๋ชป ์–ป์–ด SameValue๊ฐ€ Boolean์œผ๋กœ ๋‚จ๋Š”๋‹ค. abiondo๊ฐ€ ๊ธฐ๋Œ„ "ํ•„๋“œ์— ์ˆจ๊ธฐ๋ฉด ๋Šฆ๊ฒŒ false๋กœ ์žฌํƒ€์ž…๋œ๋‹ค"๋Š” ๋™์ž‘์ด 11.2์—์„  ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค(์ „ phase ์ถ”์ ์œผ๋กœ ํ™•์ธ โ€” SameValue๋Š” SimplifiedLowering๊นŒ์ง€ Boolean).

๋ฒฝ 2 โ€” ์ขํžŒ ์ˆœ๊ฐ„ abort. ์ž์—ฐ ํ‹ฐ์–ด์—…์€ ๋†€๋ž๊ฒŒ๋„ ๋ฒฝ 1์„ ํ†ต๊ณผํ•œ๋‹ค โ€” b๊ฐ€ ํด๋”ฉ ์•ˆ ๋˜๊ณ (store๋กœ live) typer๊ฐ€ Range(0,0)์œผ๋กœ ์ขํžŒ๋‹ค. abiondo๊ฐ€ ํ•„์š”๋กœ ํ•œ ์กฐ๊ฑด์„ ๋‹ค ๋งŒ์กฑํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ •ํ™•ํžˆ ๊ทธ "typer๊ฐ€ ์ขํ˜”๋‹ค"๊ฐ€ kAbortOnOutOfBounds๋ฅผ ๋ถ€๋ฅธ๋‹ค(๋ฒฝ 2). ์˜› V8์—์„  ์ขํž˜ = ๊ฒ€์‚ฌ ์ œ๊ฑฐ = wild OOB์˜€๋Š”๋ฐ, 11.2์—์„  ์ขํž˜ = abort ๊ฐ€๋“œ๋‹ค.

๊ทธ๋ž˜์„œ ๋‘ ๋ฒฝ ์‚ฌ์ด์— ํ†ต๊ณผ ์ง€์ ์ด ์—†๋‹ค. ์•ˆ ์ขํžˆ๋ฉด ํด๋”ฉ ์•ˆ ๋ผ๋„ ๊ฒ€์‚ฌ๊ฐ€ ์‚ด์•„ deopt(undefined), ์ขํžˆ๋ฉด ๊ฒ€์‚ฌ๊ฐ€ abort. ๊ทธ ์‚ฌ์ด "๊ฒ€์‚ฌ๊ฐ€ ํ†ต์งธ๋กœ ์‚ฌ๋ผ์ง€๋Š”" ๊ตฌ๊ฐ„์„ 11.2๊ฐ€ ๋ฉ”์›Œ๋ฒ„๋ ธ๋‹ค.

๋‘ ๋ฐฉํ–ฅ์˜ ๋ฒฝ โ€” foldโ†”live(๋ฒฝ1)์™€ ์ขํžŒ ์ˆœ๊ฐ„ abort(๋ฒฝ2) ์‚ฌ์ด์— ํ†ต๊ณผ ์ง€์ ์ด ์—†๋‹ค
๋‘ ๋ฐฉํ–ฅ์˜ ๋ฒฝ โ€” foldโ†”live(๋ฒฝ1)์™€ ์ขํžŒ ์ˆœ๊ฐ„ abort(๋ฒฝ2) ์‚ฌ์ด์— ํ†ต๊ณผ ์ง€์ ์ด ์—†๋‹ค

๐Ÿ› ๏ธ ๊ทธ๋Ÿผ ์š”์ฆ˜์€ ์–ด๋–ป๊ฒŒ ๋šซ๋‚˜ โ€” ๋ฒ„๊ทธ ํด๋ž˜์Šค๊ฐ€ ๊ฐˆ๋ ธ๋‹ค

์—ฌ๊ธฐ์„œ ์ž์—ฐ์Šค๋Ÿฌ์šด ์˜๋ฌธ โ€” ๊ทธ๋Ÿผ ํ•˜๋“œ๋‹ ์ผœ์ง„ ์š”์ฆ˜ V8์€ ์•„๋ฌด๋„ ๋ชป ๋šซ๋‚˜? ์•„๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๋ฒ„๊ทธ ํด๋ž˜์Šค๊ฐ€ ๊ฐˆ๋ ธ๋‹ค. ๊ฐ™์€ ์‹œ๊ธฐ(ํ•˜๋“œ๋‹+์ƒŒ๋“œ๋ฐ•์Šค ON) V8์„ ์‹ค์ œ๋กœ ๋šซ์€ ์ต์Šค ๋‘˜์„ ๋ณด๋ฉด ๋‹ต์ด ๋ณด์ธ๋‹ค.

  • Google v8CTF (Issue 1472121) โ€” IC clone ๋ฒ„๊ทธ(๊ฐ์ฒด ๋ ˆ์ด์•„์›ƒ ๋ถˆ์ผ์น˜). ๊ธ€์ด ๋ช…์‹œํ•œ๋‹ค: "representation checks๋กœ ์šฐํšŒ โ€” typer range bug๊ฐ€ ์•„๋‹ˆ๋‹ค. ์†์ƒ์ด nominal bounds๋ฅผ ์–ด๊ธฐ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ๊ทธ bounds๊ฐ€ ํ•ด์„๋˜๋Š” ๋ฐฉ์‹์— ์žˆ์–ด์„œ abort-on-bounds๋ฅผ ์šฐํšŒํ•œ๋‹ค." ์ฆ‰ ๋ฐฐ์—ด์˜ length/capacity ํ•„๋“œ๋ฅผ ์ง์ ‘ ์†์ƒ์‹œํ‚จ ๋’ค, ๊ทธ (์†์ƒ๋œ) ๊ธธ์ด ์•ˆ์˜ ์ธ๋ฑ์Šค๋กœ ์ ‘๊ทผํ•œ๋‹ค. ์ธ๋ฑ์Šค๊ฐ€ "in-bounds"๋ผ kAbortOnOutOfBounds๊ฐ€ ๋ฐœ๋™ํ•  ์ผ์ด ์—†๋‹ค.
  • openECSC (Array.xor) โ€” valueOf() ๋ถ€์ˆ˜ํšจ๊ณผ๋กœ ์ธ์ž ๊ฐ•์ œ๋ณ€ํ™˜๊ณผ ํƒ€์ž… ๊ฒ€์‚ฌ ์‚ฌ์ด์— ๋ฐฐ์—ด์˜ element kind๋ฅผ ๋ฐ”๊พธ๋Š” type confusion. ์—ญ์‹œ length๋ฅผ ์ง์ ‘ ์†์ƒ์‹œ์ผœ OOB ๋ฐฐ์—ด์„ ๋งŒ๋“ ๋‹ค.

์กฐ๊ธˆ ๋” ๊ตฌ์ฒด์ ์œผ๋กœ, v8CTF Issue 1472121์˜ ์ฒด์ธ์ด ์ „ํ˜•์ ์ด๋‹ค. clone IC ๋ฒ„๊ทธ๋กœ source ๊ฐ์ฒด์˜ in-object ํ”„๋กœํผํ‹ฐ๋“ค์ด target ๊ฐ์ฒด์˜ ๋” ์ž‘์€ ์Šฌ๋กฏ์— ๊ทธ๋Œ€๋กœ ๋ณต์‚ฌ๋ผ, 5~9๋ฒˆ ํ”„๋กœํผํ‹ฐ๊ฐ€ ํ”„๋กœํผํ‹ฐ ์Šคํ† ์–ด ํฌ์ธํ„ฐยท๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์™€ ๊ฒน์นœ๋‹ค. ์ด ๊ฒน์นจ์œผ๋กœ ์ธ์ ‘ ๋ฐฐ์—ด์˜ length ํ•„๋“œ๋ฅผ ํฐ ๊ฐ’์œผ๋กœ ๋ฎ์œผ๋ฉด ๊ทธ๊ฒŒ ๊ณง OOB ๋ฐฐ์—ด์ด๋‹ค. ๊ฑฐ๊ธฐ์„œ๋ถ€ํ„ฐ๋Š” ์ •์„ โ€” OOB ๋ฐฐ์—ด๋กœ ์˜† ๊ฐ์ฒด ์ฃผ์†Œ๋ฅผ ์ฝ์–ด addrof, ArrayBuffer์˜ (40๋น„ํŠธ ์ƒŒ๋“œ๋ฐ•์Šค) backing ํฌ์ธํ„ฐยทํฌ๊ธฐ๋ฅผ ๋ฎ์–ด ์ผ€์ด์ง€ ๋‚ด๋ถ€ ์ž„์˜ R/W, ๋งˆ์ง€๋ง‰์œผ๋กœ WASM์˜ RWX ์˜์—ญ์— ROP/์…ธ์ฝ”๋“œ๋ฅผ ์–น์–ด ์ƒŒ๋“œ๋ฐ•์Šค ํƒˆ์ถœ. ์‹œ์ž‘์ ๋งŒ ๋‹ค๋ฅผ ๋ฟ ๋’ท๋ถ€๋ถ„์€ ์šฐ๋ฆฌ๊ฐ€ 1ํŽธ์—์„œ ๋งคํ•‘ํ•œ ๊ฒƒ๊ณผ ๊ฐ™์€ ๊ธธ์ด๋‹ค.

openECSC์˜ Array.xor๋Š” ๋ฒ„๊ทธ ๋ชจ์–‘์ด ๋˜ ๋‹ค๋ฅด๋‹ค โ€” TOCTOU(๊ฒ€์‚ฌ-์‚ฌ์šฉ ์‹œ์ ์ฐจ). ๋นŒํŠธ์ธ์ด ์ธ์ž๋ฅผ ์ˆซ์ž๋กœ ๊ฐ•์ œ๋ณ€ํ™˜ํ•˜๊ธฐ ์ „์— ๋ฐฐ์—ด์˜ element kind๊ฐ€ PACKED_DOUBLE์ธ์ง€ ๊ฒ€์‚ฌํ•˜๋Š”๋ฐ, ๊ทธ ์ธ์ž๊ฐ€ ์•…์˜์  valueOf๋ฅผ ๊ฐ€์ง„ ๊ฐ์ฒด๋ผ ๊ฐ•์ œ๋ณ€ํ™˜ ๋„์ค‘ arr[0] = {}๋กœ ๋ฐฐ์—ด์„ PACKED_ELEMENTS(๊ฐ์ฒด ํฌ์ธํ„ฐ ์ €์žฅ)๋กœ ์ „์ด์‹œ์ผœ ๋ฒ„๋ฆฐ๋‹ค. ๊ฒ€์‚ฌ๋Š” ํ†ต๊ณผํ–ˆ๊ณ , ์ •์ž‘ XOR ์—ฐ์‚ฐ์€ ๊ฐ์ฒด ํฌ์ธํ„ฐ๋ฅผ raw 64๋น„ํŠธ๋กœ ์ฃผ๋ฌด๋ฅธ๋‹ค โ†’ ํฌ์ธํ„ฐ ์†์ƒ โ†’ ๊ฐ€์งœ ๋ฐฐ์—ด โ†’ OOB. ์—ฌ๊ธฐ์„œ๋„ typer๋ฅผ ์†์ด๋Š” ๊ฒŒ ์•„๋‹ˆ๋‹ค. ๊ฒ€์‚ฌ์™€ ์‹ค์ œ ๋™์ž‘ ์‚ฌ์ด์˜ ์‹œ์ ์ฐจ๋ฅผ ๋น„์ง‘์–ด ๊ฐ์ฒด์˜ ์ข…๋ฅ˜๋ฅผ ๋ฐ”๊พผ๋‹ค. ๊ฐ’-range ๋ฒ„๊ทธ๊ฐ€ "typer์˜ ๋ฏฟ์Œ"์„ ๊ณต๊ฒฉํ•œ๋‹ค๋ฉด, ์ด ๋‘˜์€ "๊ฐ์ฒด์˜ ํ˜•ํƒœ/์ข…๋ฅ˜"๋ฅผ ์ง์ ‘ ์–ด๊ทธ๋Ÿฌ๋œจ๋ฆฐ๋‹ค โ€” kAbortOnOutOfBounds๊ฐ€ ์†๋Œˆ ์ˆ˜ ์—†๋Š” ์ง€์ ์ด๋‹ค.

ํ•ต์‹ฌ์€ ์ด๊ฑฐ๋‹ค โ€” ์ด ์ต์Šค๋“ค์€ bounds ๊ฒ€์‚ฌ๋ฅผ ์šฐํšŒํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ฒ€์‚ฌ๊ฐ€ ๋ณด๋Š” length ์ž์ฒด๋ฅผ ๊ฑฐ์ง“์œผ๋กœ ๋งŒ๋“ ๋‹ค. typer๋ฅผ ์†์—ฌ "์ž‘์€ ์ธ๋ฑ์Šค"๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ๋ฒ„๊ทธ๋กœ length ํ•„๋“œ๋ฅผ ์ง์ ‘ ํ‚ค์›Œ ์ธ๋ฑ์Šค๊ฐ€ ์ง„์งœ๋กœ in-bounds๊ฐ€ ๋˜๊ฒŒ ํ•œ๋‹ค. kAbortOnOutOfBounds๋Š” "์ธ๋ฑ์Šค โ‰ฅ length"์ผ ๋•Œ๋งŒ ํŠธ๋žฉ์ด๋‹ˆ, length๋ฅผ ํ‚ค์›Œ๋‘๋ฉด ๋ฐœ๋™ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ฆ‰ 11.2์˜ ํ•˜๋“œ๋‹์€ "typer ์ฆ๋ช…์œผ๋กœ ๊ฒ€์‚ฌ๋ฅผ ์—†์• ๋Š”" ํ˜•ํƒœ๋งŒ ์ •์กฐ์ค€ํ•˜์ง€, "๊ฒ€์‚ฌ๊ฐ€ ์ฐธ์กฐํ•˜๋Š” length๋ฅผ ์†์ƒ์‹œํ‚ค๋Š”" ํ˜•ํƒœ๋Š” ์†๋Œ€์ง€ ์•Š๋Š”๋‹ค โ€” ์†๋Œˆ ์ˆ˜๊ฐ€ ์—†๋‹ค(๊ทธ๊ฑด ์ •์ƒ ์ฝ”๋“œ์˜ ์ •์ƒ ๋™์ž‘์ด๋‹ˆ๊นŒ).

๊ทธ๋Ÿฐ๋ฐ length/element-kind๋ฅผ ์ง์ ‘ ์†์ƒ์‹œํ‚ค๋ ค๋ฉด OOB ์“ฐ๊ธฐ๋‚˜ element-kind ํ˜ผ๋™ ๊ฐ™์€ ์ดˆ๊ธฐ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๊ฐ€ ํ•„์š”ํ•˜๋‹ค. Math.exp ๋ฒ„๊ทธ๊ฐ€ ์ฃผ๋Š” ๊ฑด ๊ฐ’-range ์–ด๊ธ‹๋‚จ(typer๋Š” ์ž‘๋‹ค ๋ฏฟ๊ณ  ๋Ÿฐํƒ€์ž„ ํผ)๋ฟ โ€” ์ด๊ฑด ์ •ํ™•ํžˆ kAbortOnOutOfBounds๊ฐ€ ์ •์กฐ์ค€ํ•ด ๋ง‰์€ ํด๋ž˜์Šค๊ณ , length๋ฅผ ์ง์ ‘ ๊ฑด๋“œ๋ฆด ์†์žก์ด๊ฐ€ ์—†๋‹ค. ๊ทธ๋ž˜์„œ ์ด ๋ฒ„๊ทธ๋Š” ํ•˜๋“œ๋‹ ์ผœ์ง„ 11.2์—์„œ ์ด ํ‘œ์ค€ ๊ฒฝ๋กœ๋กœ๋Š” ์•ˆ ์ž๋ž€๋‹ค.

๋ฒ„๊ทธ ํด๋ž˜์Šค์˜ˆํ•˜๋“œ๋‹ 11.2์—์„œ
๊ฐ’-range (typer๊ฐ€ ๊ฐ’์˜ ๋ฒ”์œ„๋ฅผ ์˜ค์ธ)Math.exp(์ด ๋ฌธ์ œ), Math.expm1(-0), lastIndexOfkAbortOnOutOfBounds์— ์ฃฝ์Œ โ€” ์ขํžŒ ์ธ๋ฑ์Šค = abort
layout / element-kind / IC (๊ฐ์ฒด ํ˜•ํƒœยท์ข…๋ฅ˜๋ฅผ ์˜ค์ธ)v8CTF clone, openECSC Array.xor์‚ด์•„์žˆ์Œ โ€” length/capacity๋ฅผ ์ง์ ‘ ์†์ƒ โ†’ in-bounds ์ ‘๊ทผ
๋ฒ„๊ทธ ํด๋ž˜์Šค๊ฐ€ ๊ฐˆ๋ ธ๋‹ค โ€” ๊ฐ’-range๋Š” abort์— ์ฃฝ๊ณ , layout/element-kind๋Š” length๋ฅผ ์ง์ ‘ ์†์ƒ์‹œ์ผœ ์‚ฐ๋‹ค
๋ฒ„๊ทธ ํด๋ž˜์Šค๊ฐ€ ๊ฐˆ๋ ธ๋‹ค โ€” ๊ฐ’-range๋Š” abort์— ์ฃฝ๊ณ , layout/element-kind๋Š” length๋ฅผ ์ง์ ‘ ์†์ƒ์‹œ์ผœ ์‚ฐ๋‹ค

๐Ÿ•ฐ๏ธ ๋ฒ„์ „์ด ์ „๋ถ€๋‹ค

๊ฐ™์€ ๋ฒ„๊ทธยท๊ฐ™์€ ๊ฐ€์ ฏ์ด๋ผ๋„ ์–ด๋А V8์ด๋ƒ๊ฐ€ ์šด๋ช…์„ ๊ฐ€๋ฅธ๋‹ค. kAbortOnOutOfBounds ์ž์ฒด๋Š” 11.2 ์†Œ์Šค์— ๋ฌด์กฐ๊ฑด ๋ฐ•ํ˜€ ์žˆ์ง€๋งŒ, ์ดํ›„ V8์€ ์ด๊ฑธ turbo_typer_hardening์ด๋ผ๋Š” READONLY ํ”Œ๋ž˜๊ทธ๋กœ ํ† ๊ธ€ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋ฐ”๊ฟจ๋‹ค. HTB GCSB 2026์˜ ํ•œ TurboFan typer ๋ฌธ์ œ๋Š” โ€” ๊ฐ™์€ ๊ฐ’-range ์–ด๊ธ‹๋‚จ์„ ์“ฐ๋Š”๋ฐ โ€” ์ถœ์ œ์ž๊ฐ€ ๊ทธ ํ”Œ๋ž˜๊ทธ๋ฅผ ๊บผ๋ฒ„๋ฆฌ๊ณ (turbo_typer_hardening=false) ์ƒŒ๋“œ๋ฐ•์Šค๊นŒ์ง€ ๋ˆ(v8_enable_sandbox=false) ๋นŒ๋“œ๋กœ ๋ƒˆ๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ ๊ฑฐ๊ธฐ์„  ์˜› ์ •์„์ด ๊ทธ๋Œ€๋กœ ํ†ตํ•œ๋‹ค.

์šฐ๋ฆฌ EXP-NaN์€ 11.2๋ผ ๊ทธ ํ”Œ๋ž˜๊ทธ ์ž์ฒด๊ฐ€ ์•„์ง ์—†๊ณ (์ฝ”๋“œ์— ๋ฌด์กฐ๊ฑด), v8_enable_sandbox=true๊นŒ์ง€ ์ผœ์ง„ ๋นŒ๋“œ๋‹ค. ๊ฐ™์€ ๋ถ€๋ฅ˜์˜ ๋ฒ„๊ทธ๊ฐ€, ํ•œ์ชฝ ๋Œ€ํšŒ์—์„  "๊ต์œก์šฉ ์›Œ๋ฐ์—…"์ด๊ณ  ๋‹ค๋ฅธ ์ชฝ(์ด ๋ฌธ์ œ)์—์„  "๋‹ค์ด์•„1"์ธ ์ด์œ ๊ฐ€ ์ •ํ™•ํžˆ ์ด ๋นŒ๋“œ ์„ค์ • ์ฐจ์ด๋‹ค.


๐Ÿค” ์™œ ํ•˜ํ•„ NaN์ธ๊ฐ€ โ€” ๊ฐ™์€ ํด๋ž˜์Šค, ๋” ๋ˆ์ ํ•œ ๊ฐ’

์ด ๋ฒ„๊ทธ๋Š” abiondo๊ฐ€ 2019๋…„์— ํ„ฐ๋œจ๋ฆฐ Math.expm1(-0) ๋ฒ„๊ทธ์™€ ์ •ํ™•ํžˆ ๊ฐ™์€ ํด๋ž˜์Šค๋‹ค โ€” "typer๊ฐ€ ์–ด๋–ค ๋นŒํŠธ์ธ์˜ ์ถœ๋ ฅ์—์„œ ํŠน์ˆ˜๊ฐ’(๊ทธ์ชฝ์€ -0, ์ด์ชฝ์€ NaN)์„ ๋น ๋œจ๋ ค, Object.is(๋นŒํŠธ์ธ, ํŠน์ˆ˜๊ฐ’)์ด typer์—” falseยท๋Ÿฐํƒ€์ž„์—” true์ธ ๋ถˆ๋ฆฌ์–ธ์ด ๋œ๋‹ค." ๊ฐ€์ ฏ์˜ ๊ณจ๊ฒฉ์ด ๋˜‘๊ฐ™๋‹ค.

๋ถ€๋™์†Œ์ˆ˜์  ์ˆ˜์˜ ๊ตฌ์„ฑ โ€” ๋ถ€ํ˜ธยท์ง€์ˆ˜ยท๊ฐ€์ˆ˜(์†Œ์ˆ˜๋ถ€). NaN๊ณผ -0์€ ์ด ๋น„ํŠธ๋“ค์˜ ํŠน์ˆ˜ํ•œ ์กฐํ•ฉ์ด๊ณ , typer๊ฐ€ ๋น ๋œจ๋ฆฌ๋Š” ๊ฒŒ ๋ฐ”๋กœ ๊ทธ ํŠน์ˆ˜ ์นธ์ด๋‹ค (์ถœ์ฒ˜: Wikimedia Commons, CC BY-SA 3.0)
๋ถ€๋™์†Œ์ˆ˜์  ์ˆ˜์˜ ๊ตฌ์„ฑ โ€” ๋ถ€ํ˜ธยท์ง€์ˆ˜ยท๊ฐ€์ˆ˜(์†Œ์ˆ˜๋ถ€). NaN๊ณผ -0์€ ์ด ๋น„ํŠธ๋“ค์˜ ํŠน์ˆ˜ํ•œ ์กฐํ•ฉ์ด๊ณ , typer๊ฐ€ ๋น ๋œจ๋ฆฌ๋Š” ๊ฒŒ ๋ฐ”๋กœ ๊ทธ ํŠน์ˆ˜ ์นธ์ด๋‹ค (์ถœ์ฒ˜: Wikimedia Commons, CC BY-SA 3.0)

๊ทธ๋Ÿฐ๋ฐ NaN์€ -0๋ณด๋‹ค ํ•œ ๊ฒน ๋” ๊ฐ‘๊ฐ‘ํ•˜๋‹ค. -0์€ ์‚ฐ์ˆ ์—์„œ ์‚ฌ์‹ค์ƒ 0์ฒ˜๋Ÿผ ํ๋ฅธ๋‹ค(-0 + x = x, ToInt32(-0)=0) โ€” ์ฆ‰ "๊ฑฐ์˜ 0์ธ ์œ ํ•œ๊ฐ’"์ด๋ผ, ๋ถˆ๋ฆฌ์–ธ์œผ๋กœ ์•ˆ ๊ฐ€๋„ ๊ฐ’ ์ž์ฒด๋ฅผ ์–ด๋–ป๊ฒŒ๋“  ํ˜๋ ค๋ณผ ์—ฌ์ง€๊ฐ€ (์˜› ๋นŒ๋“œ์—์„ ) ์žˆ์—ˆ๋‹ค. ๋ฐ˜๋ฉด NaN์€ ๋ชจ๋“  ์‚ฐ์ˆ ์—์„œ NaN์œผ๋กœ ์ „ํŒŒ๋œ๋‹ค. NaN + x = NaN, NaN * 0 = NaN, Math.max(NaN, k)๋„ (๋ณด์ • ์—†์œผ๋ฉด) NaN, ๋ฃจํ”„์—์„œ i += NaN๋„ NaN. ๊ทธ๋ฆฌ๊ณ  ๊ทธ NaN์„ ์ •์ˆ˜๋กœ ๋–จ๊ตฌ๋ฉด ToInt32(NaN)=0(์ž‘์•„์ง) ์•„๋‹ˆ๋ฉด cvttsd2si(NaN)=INT_MIN(์Œ์ˆ˜โ†’๊ฑธ๋Ÿฌ์ง), ๋น„๊ต์— ๋„ฃ์œผ๋ฉด ํ•ญ์ƒ false(ํด๋”ฉ). ํฐ ์œ ํ•œ ์ •์ˆ˜๋กœ ์ž๋ผ๋Š” ๊ธธ์ด ์‚ฐ์ˆ  ์–ด๋””์—๋„ ์—†๋‹ค.

๊ทธ๋ž˜์„œ NaN ๋ฒ„๊ทธ๋Š” ์‚ฌ์‹ค์ƒ ๋ถˆ๋ฆฌ์–ธ ๊ฐ€์ ฏ ํ•˜๋‚˜์— ์šด๋ช…์„ ๊ฑด๋‹ค. min/maxยทinductionยทlength-construction ๊ฐ™์€ "๊ฐ’์œผ๋กœ ํ˜๋ฆฌ๋Š”" ์šฐํšŒ๊ฐ€ 1ํŽธยท2ํŽธ ๋‚ด๋‚ด ์ „๋ฉธํ•œ ๊ฒŒ ์šฐ์—ฐ์ด ์•„๋‹ˆ๋‹ค โ€” NaN์˜ ๋ˆ์ ํ•จ์ด ๊ทธ ๊ธธ๋“ค์„ ์›์ฒœ๋ด‰์‡„ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ ์œ ์ผํ•œ ๋ถˆ๋ฆฌ์–ธ ๊ฐ€์ ฏ์ด 11.2์˜ kAbortOnOutOfBounds์— ์ •์กฐ์ค€๋ผ ๋ง‰ํ˜”๋‹ค. ์ถœ์ œ์ž๊ฐ€ -0์ด ์•„๋‹ˆ๋ผ NaN์„, ๊ทธ๊ฒƒ๋„ ํ•˜๋“œ๋‹ ์ผœ์ง„ 11.2์— ์˜ฌ๋ฆฐ ์„ ํƒ์€ โ€” ๊ณ ์ „ ๊ฐ€์ ฏ์ด ์™œ ์•ˆ ํ†ตํ•˜๋Š”์ง€ ์†Œ์Šค๊นŒ์ง€ ๋‚ด๋ ค๊ฐ€ ๋ถ€๋”ชํ˜€๋ณด๋ผ๋Š”, ๊ฝค ์˜๋„์ ์ธ ๋‚œ์ด๋„ ์„ค๊ณ„๋กœ ์ฝํžŒ๋‹ค.


๐Ÿชœ ๊ทธ ๋’ค๋Š” ๋‹ค ์ค€๋น„๋ผ ์žˆ๋‹ค โ€” ์ฒซ OOB๋งŒ ์—ด๋ฆฌ๋ฉด

์ด ๋ถ€๊ฒ€์˜ ๊ฒฐ๋ก ์€ "์ฒซ OOB๊ฐ€ ์•ˆ ์—ด๋ฆฐ๋‹ค"์ง€๋งŒ, ๋งŒ์•ฝ ์—ด๋ฆฐ๋‹ค๋ฉด ๊ทธ ๋’ค๋Š” 11.2 ๊ธฐ์ค€์œผ๋กœ ์ด๋ฏธ ์ •์„์ด ์žกํ˜€ ์žˆ๋‹ค(1ํŽธ์—์„œ ๋งคํ•‘ํ•œ ํ›„๋ฐ˜๋ถ€). ๋ฌด๊ฒŒ์ค‘์‹ฌ์ด ์–ด๋”” ์ ๋ ค ์žˆ๋Š”์ง€ ๋ณด์ด๊ฒŒ ๊ธฐ๋กํ•ด๋‘”๋‹ค.

  1. ์•ˆ์ • OOB ๋ฐฐ์—ด. ์ฒซ OOB๋กœ ์ธ์ ‘ ๋ฐฐ์—ด์˜ length/capacity๋ฅผ ๊ฑฐ๋Œ€๊ฐ’์œผ๋กœ ๋ฎ์–ด, ๊ฒ€์‚ฌ์— ์•ˆ ๊ฑธ๋ฆฌ๋Š” "๊ธธ์ด ๋ถ€ํ’€๋ฆฐ" float ๋ฐฐ์—ด์„ ๋งŒ๋“ ๋‹ค. (์•ž์„œ ๋ณธ ํ˜„์—ญ ์ต์Šค๋“ค์ด ์ฒ˜์Œ๋ถ€ํ„ฐ ํ•˜๋Š” ๊ฒŒ ๋ฐ”๋กœ ์ด length ์†์ƒ์ด๋‹ค โ€” ์šฐ๋ฆฌ๋งŒ ๊ทธ ์ž…๊ตฌ๊ฐ€ ๋ง‰ํ˜”๋‹ค.)
  2. addrof / fakeobj. float ๋ฐฐ์—ด(PACKED_DOUBLE)๊ณผ object ๋ฐฐ์—ด(PACKED_ELEMENTS)์„ ์ธ์ ‘์‹œ์ผœ, object๋ฅผ ๋„ฃ๊ณ  float๋กœ ์ฝ์œผ๋ฉด ์••์ถ• ํฌ์ธํ„ฐ(์ฃผ์†Œ)๊ฐ€ ๋ถ€๋™์†Œ์ˆ˜์  ๋น„ํŠธ๋กœ ๋‚˜์˜จ๋‹ค(addrof). ๋ฐ˜๋Œ€๋กœ ๊ฐ€์งœ ์ฃผ์†Œ๋ฅผ ์จ๋„ฃ๊ณ  object๋กœ ์ฝ์œผ๋ฉด ์ž„์˜ ์ฃผ์†Œ๋ฅผ ๊ฐ์ฒด๋กœ ๋‘”๊ฐ‘(fakeobj).
  3. ์ผ€์ด์ง€ ๋‚ด๋ถ€ ์ž„์˜ R/W. ArrayBuffer์˜ backing_store(JSArrayBuffer+0x14)๋ฅผ ์†์ƒ์‹œ์ผœ ๊ทธ ์œ„ DataView/TypedArray๋กœ 4GB ์ผ€์ด์ง€ ๋‚ด๋ถ€๋ฅผ ์ž„์˜๋กœ ์ฝ๊ณ  ์“ด๋‹ค. (11.2๋Š” backing_store๊ฐ€ caged sandboxed pointer๋ผ ์ด๊ฒƒ๋งŒ์œผ๋ก  ์ผ€์ด์ง€๋ฅผ ๋ชป ๋ฒ—์–ด๋‚œ๋‹ค.)
  4. WASM ์ƒŒ๋“œ๋ฐ•์Šค ํƒˆ์ถœ. ์ผ€์ด์ง€ ๋ฐ–์œผ๋กœ ๋‚˜๊ฐ€๋Š” ์œ ์ผํ•œ ์†์žก์ด๊ฐ€ WasmInstanceObject.jump_table_start(instance+~0x60) โ€” ์ผ€์ด์ง€ ๋ฐ– RWX WASM ์ฝ”๋“œ ํŽ˜์ด์ง€๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” raw 64๋น„ํŠธ ํฌ์ธํ„ฐ๋‹ค(11.2์—” ์•„์ง raw; WASM trusted-space ๋ถ„๋ฆฌ๋Š” 2024/~v8 12.5). ์ผ€์ด์ง€ ๋‚ด๋ถ€ R/W๋Š” ์ผ€์ด์ง€ ๋ฐ– RWX์— ์ง์ ‘ ๋ชป ๋‹ฟ์œผ๋‹ˆ, jump_table_start๋ฅผ export ํ•จ์ˆ˜ ๋ณธ๋ฌธ์˜ mov [rax],rdx(48 89 10) Liftoff ๊ฐ€์ ฏ ์ฃผ์†Œ๋กœ ๋ฎ๊ณ , ์‹œ๊ทธ๋‹ˆ์ฒ˜ ๋ฏธ๊ฒ€์ฆ export ํ˜ธ์ถœ๋กœ ์ธ์ž๋ฅผ rax/rdx์— ์‹ค์–ด ์ผ€์ด์ง€ ๋ฐ–์— 8๋ฐ”์ดํŠธ์”ฉ ์…ธ์ฝ”๋“œ๋ฅผ ํ˜๋ ค๋„ฃ์€ ๋’ค, jump_table_start๋ฅผ ๋ณต๊ตฌํ•˜๊ณ  export(main)๋ฅผ ํ˜ธ์ถœํ•ด ์‹คํ–‰ํ•œ๋‹ค. (์ฒด์ธ: JSFunction โ†’ SFI(+0x0C) โ†’ WasmExportedFunctionData(+0x08) โ†’ instance(+0x10).)
  5. ์…ธ์ฝ”๋“œ โ†’ flag. ํ‘œ์ค€ x64 execve("./flag_reader-<md5>")(์ƒ๋Œ€๊ฒฝ๋กœ, ์„œ๋ฒ„ cwd=/home/pwn). d8์—” read/load/os ๋“ฑ ์ž…์ถœ๋ ฅ์ด ๋‹ค ์ œ๊ฑฐ๋์œผ๋‹ˆ, ์ด setuid ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ๋งŒ์ด flag๋กœ ๊ฐ€๋Š” ๊ธธ์ด๋‹ค.

์™œ 4๋ฒˆ(WASM ํƒˆ์ถœ)์ด ๋”ฐ๋กœ ํ•„์š”ํ•œ์ง€๋Š” V8 ์ƒŒ๋“œ๋ฐ•์Šค ๊ตฌ์กฐ๋ฅผ ๋ณด๋ฉด ํ•œ๋ˆˆ์— ๋“ค์–ด์˜จ๋‹ค. ์ƒŒ๋“œ๋ฐ•์Šค๋Š” V8 ํž™์„ ํ•˜๋‚˜์˜ ํฐ ์˜์—ญ(์ผ€์ด์ง€)์— ๊ฐ€๋‘๊ณ , ๊ทธ ์•ˆ์˜ ๊ฐ์ฒด๋Š” ์„œ๋กœ๋ฅผ ์˜คํ”„์…‹์œผ๋กœ๋งŒ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ํ•œ๋‹ค. ์ผ€์ด์ง€ ๋ฐ– ๊ฐ์ฒด(์˜ˆ: ArrayBuffer์˜ ์‹ค์ œ backing)๋Š” ์ง์ ‘ ํฌ์ธํ„ฐ๊ฐ€ ์•„๋‹ˆ๋ผ ํฌ์ธํ„ฐ ํ…Œ์ด๋ธ”์˜ ์ธ๋ฑ์Šค๋กœ๋งŒ ๋‹ฟ๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ์ผ€์ด์ง€ ์•ˆ์—์„œ ์•„๋ฌด๋ฆฌ ์ž„์˜ R/W๊ฐ€ ์žˆ์–ด๋„, ๊ทธ๊ฒƒ๋งŒ์œผ๋ก  ์ผ€์ด์ง€ ๋ฐ– ์ง„์งœ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ฑด๋“œ๋ฆด ์ˆ˜ ์—†๋‹ค โ€” ๋ณ„๋„์˜ ํƒˆ์ถœ๊ตฌ๊ฐ€ ํ•„์š”ํ•œ ์ด์œ ๋‹ค.

V8 ์ƒŒ๋“œ๋ฐ•์Šค โ€” ์ผ€์ด์ง€ ์•ˆ ๊ฐ์ฒด๋Š” ์„œ๋กœ๋ฅผ ์˜คํ”„์…‹์œผ๋กœ ์ฐธ์กฐํ•˜๊ณ , ์ผ€์ด์ง€ ๋ฐ– ๊ฐ์ฒด๋Š” ํฌ์ธํ„ฐ ํ…Œ์ด๋ธ” ์ธ๋ฑ์Šค๋กœ๋งŒ ๋‹ฟ๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ์ผ€์ด์ง€ ๋‚ด๋ถ€ R/W๋งŒ์œผ๋ก  ๋ฐ–์„ ๋ชป ๊ฑด๋“œ๋ฆฐ๋‹ค (์ถœ์ฒ˜: V8 ๊ณต์‹ ๋ธ”๋กœ๊ทธ v8.dev/blog/sandbox)
V8 ์ƒŒ๋“œ๋ฐ•์Šค โ€” ์ผ€์ด์ง€ ์•ˆ ๊ฐ์ฒด๋Š” ์„œ๋กœ๋ฅผ ์˜คํ”„์…‹์œผ๋กœ ์ฐธ์กฐํ•˜๊ณ , ์ผ€์ด์ง€ ๋ฐ– ๊ฐ์ฒด๋Š” ํฌ์ธํ„ฐ ํ…Œ์ด๋ธ” ์ธ๋ฑ์Šค๋กœ๋งŒ ๋‹ฟ๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ์ผ€์ด์ง€ ๋‚ด๋ถ€ R/W๋งŒ์œผ๋ก  ๋ฐ–์„ ๋ชป ๊ฑด๋“œ๋ฆฐ๋‹ค (์ถœ์ฒ˜: V8 ๊ณต์‹ ๋ธ”๋กœ๊ทธ v8.dev/blog/sandbox)

11.2์—์„œ ์ด ์ผ€์ด์ง€๋ฅผ ๋ฒ—์–ด๋‚˜๋Š” ์œ ์ผํ•œ ํ‹ˆ์ด jump_table_start์˜€๋‹ค โ€” ์ผ€์ด์ง€ ๋ฐ– RWX ํŽ˜์ด์ง€๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š”, ์•„์ง ๋ณดํ˜ธ๋ฐ›์ง€ ์•Š๋Š” raw ํฌ์ธํ„ฐ. (2024๋…„ WASM trusted-space ๋ถ„๋ฆฌ๋กœ ๊ทธ ํ‹ˆ๋งˆ์ € ๋‹ซํ˜”๋‹ค.)

์ •๋ง 1๋ฒˆ(์ฒซ OOB)๋งŒ ์—ด๋ฆฌ๋ฉด 2~5๋ฒˆ์€ ๋ง‰ํžˆ๋Š” ๋ฐ ์—†์ด ํ๋ฅธ๋‹ค. ๊ทธ๋ž˜์„œ ์ด ๋ฌธ์ œ์˜ ๋ฌด๊ฒŒ์ค‘์‹ฌ์€ ์ „์ ์œผ๋กœ "์ฒซ OOB๋ฅผ ์–ด๋–ป๊ฒŒ ์—ฌ๋А๋ƒ" ์— ์ ๋ ค ์žˆ๊ณ , ๊ทธ๊ฒŒ ์ด ๋ถ€๊ฒ€์˜ ์ฃผ์ œ์˜€๋‹ค โ€” ๊ทธ๋ฆฌ๊ณ  11.2์˜ ๋‹ต์€ "ํ‘œ์ค€ ๊ฐ’-range ๊ฒฝ๋กœ๋กœ๋Š” ์•ˆ ์—ฐ๋‹ค"์˜€๋‹ค.


๐Ÿ“ ๊ฒฐ๋ก  โ€” ํ•œ ์‹œ๋Œ€์˜ ์ข…๋ฃŒ

์ด ๋ถ€๊ฒ€์˜ ๊ฒฐ๋ก ์€ ๋ช…๋ฃŒํ•˜๋‹ค. Math.exp์˜ NaN ์˜คํƒ€์ž… ๊ฐ™์€ ๊ฐ’-range typer ๋ฒ„๊ทธ๋Š”, ํ•˜๋“œ๋‹์ด ์ผœ์ง„ V8 11.2์—์„œ ํ‘œ์ค€ OOB ๊ฒฝ๋กœ๋กœ๋Š” ์ฃฝ์€ ํด๋ž˜์Šค๋‹ค.

  • ์˜› V8: typer๊ฐ€ ์ธ๋ฑ์Šค๋ฅผ in-bounds๋กœ ์ฆ๋ช…ํ•˜๋ฉด CheckBounds๋ฅผ ์ œ๊ฑฐ โ†’ ๊ฒ€์‚ฌ ์—†๋Š” wild OOB. abiondoยทdoar-eยทQuick-Maffs๊ฐ€ ์‚ฐ ์‹œ๋Œ€.
  • V8 11.2: ์ฆ๋ช…ํ•˜๋ฉด ์ œ๊ฑฐ๊ฐ€ ์•„๋‹ˆ๋ผ abort. doar-e์‹ OOB-load-mode ์šฐํšŒ๊นŒ์ง€ ์†Œ์Šค ์ฃผ์„์œผ๋กœ ์ฝ• ์ง‘์–ด ๋ง‰์Œ. ๊ทธ๋ž˜์„œ ์ขํžŒ ์ธ๋ฑ์Šค๋Š” ํŠธ๋žฉ, ์•ˆ ์ขํžŒ ์ธ๋ฑ์Šค๋Š” deopt โ€” wild OOB ๊ตฌ๊ฐ„์ด ์‚ฌ๋ผ์กŒ๋‹ค.
  • ๊ทธ๋ž˜์„œ ํ˜„์—ญ ์ต์Šค๋Š” ๊ฐ’-range๋ฅผ ๋ฒ„๋ฆฌ๊ณ  length/element-kind/IC๋ฅผ ์ง์ ‘ ์†์ƒ์‹œํ‚ค๋Š” ๋ฒ„๊ทธ๋กœ ๊ฐˆ์•„ํƒ”๋‹ค.

JIT ์ปดํŒŒ์ผ๋Ÿฌ์—์„œ ํƒ€์ž…์€ "ํžŒํŠธ"๊ฐ€ ์•„๋‹ˆ๋ผ ์•ˆ์ „ ๊ฒ€์‚ฌ๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๊ทผ๊ฑฐ๋‹ค. ๊ทธ ์ œ๊ฑฐ๋ฅผ "์ œ๊ฑฐ ๋Œ€์‹  abort"๋กœ ๋ฐ”๊พผ ํ•œ ์ค„(new_flags |= kAbortOnOutOfBounds)์ด, typer-ํ˜ผ๋™ ์ต์Šค์˜ ํ•œ ์„ธ๋Œ€๋ฅผ ํ†ต์งธ๋กœ ๋‹ซ์•˜๋‹ค. ์ •ํ™•ํžˆ ๊ทธ ํ•œ ์ค„์„ ์ค„ ๋ฒˆํ˜ธ๋กœ ์งš์–ด๋‚ด๋Š” ๊ฒƒ โ€” ๊ทธ๊ฒŒ ์ด ๋‹ค์ด์•„1 ๋ฌธ์ œ์˜ ์ง„์งœ ํ•™์Šต ํฌ์ธํŠธ๋ผ๊ณ  ๋ณธ๋‹ค. 100% ๋ง‰๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ(layout ํด๋ž˜์Šค๋Š” ์‚ด์•„์žˆ๋‹ค), "typer ์ฆ๋ช… ๊ธฐ๋ฐ˜ ๊ฒ€์‚ฌ ์ œ๊ฑฐ"๋ผ๋Š” ํŠน์ • ์ต์Šค ํ˜•ํƒœ๋ฅผ ๋น„์‹ธ๊ฒŒ ๋งŒ๋“œ๋Š” best-effort ํ•˜๋“œ๋‹์ด, ํ•œ ๋ฒ„๊ทธ๋ฅ˜์˜ ๊ณต๊ฒฉ ๋‚œ์ด๋„๋ฅผ ์›Œ๋ฐ์—…์—์„œ ๋‹ค์ด์•„1๋กœ ๋Œ์–ด์˜ฌ๋ฆฐ๋‹ค.

๋ฒ„๊ทธ๋ฅผ ๋ชป ๋šซ์—ˆ๋‹ค๊ณ  ๋นˆ์†์€ ์•„๋‹ˆ๋‹ค. "์™œ ์•ˆ ๋šซ๋ฆฌ๋‚˜"๋ฅผ ์ค„ ๋ฒˆํ˜ธ๋กœ ๋‹ตํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ ์ž์ฒด๊ฐ€, ์ด ๋นŒ๋“œ์˜ ๋ฐฉ์–ด๊ฐ€ ์–ด๋””์— ์–ด๋–ป๊ฒŒ ์ณ์ ธ ์žˆ๋Š”์ง€์˜ ์ง€๋„๋‹ค. ๊ทธ ์ง€๋„๋ฅผ ๋‹ค์Œ ์ ˆ์— ์ •๋ฆฌํ•œ๋‹ค.


๐Ÿ›ก๏ธ ๋ฐฉ์–ด ๊ด€์  โ€” ์ด ๋ถ€๊ฒ€์ด ๊ทธ๋ฆฐ ์ง€๋„

  • typer๋Š” ์ฐจ๋ถ„ ํผ์ง•๊ณผ ํƒ€์ž… ๋‹จ์–ธ์œผ๋กœ ์ง€ํ‚จ๋‹ค. ์–ด๋–ค ๋นŒํŠธ์ธ์˜ ์ถœ๋ ฅ ํƒ€์ž…์„ ์ขํž ๋• NaNยท-0ยทยฑInfinity ๊ฒฝ๊ณ„๋ฅผ ๋ฐ˜๋“œ์‹œ ํฌํ•จํ•˜๋Š”์ง€ ๊ฒ€์ฆํ•œ๋‹ค(V8์—” --assert-types๋กœ ๋Ÿฐํƒ€์ž„ ํƒ€์ž… ๋‹จ์–ธ์„ ์ผœ๋Š” ์˜ต์…˜๋„ ์žˆ๋‹ค). ์ด ๋ฒ„๊ทธ๋Š” ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ์‹ค์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํ•œ ๊ฐ’์— ๋Œ€ํ•ด ๊ฐ€์ง„ ๋ฏฟ์Œ์˜ ์˜ค๋ฅ˜์˜€๋‹ค โ€” Math.exp์˜ ๋ฐ˜ํ™˜์—์„œ NaN ํ•œ ์นธ์„ ๋บ€ ๊ฒƒ๋ฟ. JIT์—์„œ ํƒ€์ž…์€ "ํžŒํŠธ"๊ฐ€ ์•„๋‹ˆ๋ผ ์•ˆ์ „ ๊ฒ€์‚ฌ๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๊ทผ๊ฑฐ๋ผ, ์‹ค์ œ๋ณด๋‹ค ์ข์€ ํƒ€์ž…์€ ๊ทธ๋Œ€๋กœ ๋ฉ”๋ชจ๋ฆฌ ์•ˆ์ „์„ฑ ๊ตฌ๋ฉ์ด ๋œ๋‹ค.
  • ์‹ฌ์ธต ๋ฐฉ์–ด๊ฐ€ ์‹ค์ œ๋กœ ์ผํ•œ๋‹ค. kAbortOnOutOfBounds๋Š” typer ๋ฒ„๊ทธ๋ฅผ 100% ๋ง‰์ง„ ๋ชปํ•œ๋‹ค(layout/element-kind ํด๋ž˜์Šค๋Š” ์‚ด์•„์žˆ๋‹ค). ํ•˜์ง€๋งŒ "typer ์ฆ๋ช… ๊ธฐ๋ฐ˜ ๊ฒ€์‚ฌ ์ œ๊ฑฐ"๋ผ๋Š” ํŠน์ • ์ต์Šค ํ˜•ํƒœ๋ฅผ ๋น„์‹ธ๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ๋งŒ์œผ๋กœ, ๊ฐ™์€ ๋ถ€๋ฅ˜ ๋ฒ„๊ทธ์˜ ๋‚œ์ด๋„๋ฅผ ์›Œ๋ฐ์—…(ํ•˜๋“œ๋‹ off ๋นŒ๋“œ)์—์„œ ๋‹ค์ด์•„1(์ด ๋ฌธ์ œ)๋กœ ๋Œ์–ด์˜ฌ๋ฆฐ๋‹ค. best-effort ํ•˜๋“œ๋‹์˜ ๊ฐ€์น˜๊ฐ€ ์ •ํ™•ํžˆ ์ด ๊ฒฉ์ฐจ๋‹ค.
  • ๋ฐฉ์–ด๋Š” ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉฐ ๋‹จ๋‹จํ•ด์ง„๋‹ค. 11.2(2023)๋Š” backing_store๊ฐ€ ์ผ€์ด์ง€์— ๋ฌถ์—ฌ ArrayBuffer๋กœ๋Š” ๋ชป ๋‚˜๊ฐ€์ง€๋งŒ jump_table_start๊ฐ€ ์•„์ง raw RWX๋ผ WASM์œผ๋กœ ํƒˆ์ถœ์ด ๊ฐ€๋Šฅํ–ˆ๋‹ค. 2024๋…„ WASM trusted-space ๋ถ„๋ฆฌ(~v8 12.5) ์ดํ›„์—” ๊ทธ ๋งˆ์ง€๋ง‰ ์†์žก์ด๋„ ๋‹ซํ˜”๋‹ค. ์ต์Šค๋ฅผ ์งค ๋• ๊ทธ ๋ฒ„์ „์˜ ๋ฉ”๋ชจ๋ฆฌ ๋ ˆ์ด์•„์›ƒ์„ ์ •ํ™•ํžˆ ํ™•์ธํ•ด์•ผ ํ•˜๊ณ , ๋ฐฉ์–ด์ž ์ž…์žฅ์—์„  ํ•œ ๊ฒน(typer)์ด ๋šซ๋ ค๋„ ๋‹ค์Œ ๊ฒน(bounds ํ•˜๋“œ๋‹ โ†’ ์ƒŒ๋“œ๋ฐ•์Šค โ†’ trusted-space)์ด ๋น„์šฉ์„ ๊ณ„์† ์˜ฌ๋ฆฌ๋„๋ก ์Œ“๋Š” ๊ฒŒ ํ•ต์‹ฌ์ด๋‹ค.
  • ๋ฒ„๊ทธ ํด๋ž˜์Šค๋ฅผ ๊ตฌ๋ถ„ํ•ด ์šฐ์„ ์ˆœ์œ„๋ฅผ ๋‘”๋‹ค. ๊ฐ’-range typer ๋ฒ„๊ทธ๋Š” kAbortOnOutOfBounds๋กœ ์‚ฌ์‹ค์ƒ ์‚ฌ์žฅ๋์ง€๋งŒ, layout/element-kind/IC/TOCTOU ๋ฒ„๊ทธ๋Š” length๋ฅผ ์ง์ ‘ ์†์ƒ์‹œ์ผœ ์‚ด์•„์žˆ๋‹ค. ํผ์ง•ยท๋ฆฌ๋ทฐ ์ž์›์„ ํ›„์ž(๊ฐ์ฒด ํ˜•ํƒœยท์ „์ดยทcloneยทvalueOf ์žฌ์ง„์ž…)์— ๋” ์Ÿ๋Š” ๊ฒŒ ํ•ฉ๋ฆฌ์ ์ด๋‹ค.

๐Ÿ“š ์ฐธ๊ณ 

์ด๋ฏธ์ง€ ์ถœ์ฒ˜ โ€” V8 ์ƒŒ๋“œ๋ฐ•์Šค: V8 ๊ณต์‹ ๋ธ”๋กœ๊ทธ(v8.dev/blog/sandbox). ๋ถ€๋™์†Œ์ˆ˜์  ๊ตฌ์„ฑ: Wikimedia Commons (CC BY-SA 3.0). ํ๋ฆ„๋„ยทํ„ฐ๋ฏธ๋„ ์บก์ณ๋Š” ์ง์ ‘ ์ œ์ž‘/์ธก์ •.


๋‹ค์Œ โ€” 3ํŽธ(์˜ˆ์ •). ์ด ๋ถ€๊ฒ€์€ "ํ‘œ์ค€ ๊ฐ’-range ๊ฒฝ๋กœ๋Š” 11.2์—์„œ ๋‹ซํ˜”๋‹ค"๊นŒ์ง€๋‹ค. ์ฒซ OOB๋ฅผ ์—ฌ๋Š” ๊ทธ ๋น„ํ‘œ์ค€ ํ•œ ์ˆ˜๋ฅผ ์ฐพ์œผ๋ฉด, ์œ„ ํ›„๋ฐ˜๋ถ€ ์ฒด์ธ์„ ๊ทธ๋Œ€๋กœ ํƒœ์›Œ ๋ผ์ด๋ธŒ flag๊นŒ์ง€ ๊ฐ€๋Š” 3ํŽธ์œผ๋กœ ์ž‡๋Š”๋‹ค.

์ด ๊ธ€์ด ๋„์›€์ด ๋๋‚˜์š”?

Comments

๋Œ“๊ธ€

0๊ฐœ

๋Œ“๊ธ€์„ ๋‚จ๊ธฐ๋ ค๋ฉด ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ด์š”. (๋„ค์ด๋ฒ„ ยท ๊ตฌ๊ธ€ ๊ณ„์ •)

๋Œ“๊ธ€ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘โ€ฆ

Related

๊ด€๋ จ ๊ธ€

3๊ฐœ
[๐Ÿ’Ž Diamond 1] Math.exp๊ฐ€ NaN์„ ์žŠ์„ ๋•Œ (4) โ€” ๊ทผ๋ฐ .at()์ด ์—ด๋ ค ์žˆ์—ˆ๋‹ค: ๋†“์นœ ๋น„-CheckBounds sink โ€” DreamHack EXP-NaN
blog

[๐Ÿ’Ž Diamond 1] Math.exp๊ฐ€ NaN์„ ์žŠ์„ ๋•Œ (4) โ€” ๊ทผ๋ฐ .at()์ด ์—ด๋ ค ์žˆ์—ˆ๋‹ค: ๋†“์นœ ๋น„-CheckBounds sink โ€” DreamHack EXP-NaN

3ํŽธ์€ "๋‚จ์€ ์ถœ๊ตฌ๋ฅผ ์ „์ˆ˜๋กœ ๋‘๋“œ๋ ธ์ง€๋งŒ ๋‹ค ๋‹ซํ˜”๋‹ค"๋กœ ๋๋‚ฌ๋‹ค. 4ํŽธ์€ ๊ทธ ๊ฒฐ๋ก ์„ ๋‚ด ์†์œผ๋กœ ๋’ค์ง‘๋Š” ๊ธฐ๋ก์ด๋‹ค โ€” 3ํŽธ์ด ๋ณธ sink๋Š” ์ „๋ถ€ bracket ์ ‘๊ทผ(`a[i]`)๊ณผ CheckBounds ๊ณ„์—ด์ด์—ˆ๋Š”๋ฐ, ๋”ฑ ํ•˜๋‚˜ ์•ˆ ๋ณธ ๊ฒŒ ์žˆ์—ˆ๋‹ค. `Array.prototype.at()`. ์ด๊ฑด ๋ณ„๋„ reducer(js-call-reducer.cc)๋ผ ๊ฒฝ๊ณ„๊ฒ€์‚ฌ๋ฅผ CheckBounds๊ฐ€ ์•„๋‹ˆ๋ผ fold ๊ฐ€๋Šฅํ•œ NumberLessThan ๋‘ ๊ฐœ + bare LoadElement ๋กœ ํ•œ๋‹ค. ํŒจ์น˜๊ฐ€ ๊ทธ ํŒŒ์ผ์„ ์•ˆ ๊ฑด๋“œ๋ ค์„œ ๊ทธ๋Œ€๋กœ ์‚ด์•„์žˆ๊ณ , reshape๋กœ typer๋ฅผ ์†์ด๋ฉด ๋‘ ๊ฐ€๋“œ๊ฐ€ ์ ‘ํ˜€ ๋ฌด๊ฐ€๋“œ OOB read๊ฐ€ ์ง„์งœ๋กœ ์—ด๋ฆฐ๋‹ค(์ž์—ฐ ํ‹ฐ์–ด์—…, exit0, ๊ฒฐ์ •์ ). ๋‹ค๋งŒ store ์ง์ด ์—†์–ด write๋Š” ์—ฌ์ „ํžˆ ๋ง‰ํ˜€ ์žˆ๊ณ (bracket=trap, TypedArray/fill/with ์ „๋ฉธ, a.length๋Š” ์น˜์œ ), readโ†’write ๋‹ค๋ฆฌ๋Š” addrof/fakeobj๋ฟ์ด๋‹ค. ํฌ์ธํ„ฐ ์••์ถ•์ด ์™œ reader ํƒ€์ž…์„ ๊ฐ€๋ฅด๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ๊ทธ fakeobj ๋ถ€ํŠธ์ŠคํŠธ๋žฉ(ํƒ€์ž…-๋งค์นญ ์ธ์ ‘)์ด ์™œ nursery monoculture์— ๋ง‰ํžˆ๋Š”์ง€ โ€” V8 reducer ๊ตฌ์กฐ๋ถ€ํ„ฐ ์‰˜ ์Šค์ƒท๊นŒ์ง€ ํ•œ ๋‹จ๊ณ„์”ฉ.
#dreamhack#ctf#pwn+7
2026-06-07#dreamhack +9
[๐Ÿ’Ž Diamond 1] Math.exp๊ฐ€ NaN์„ ์žŠ์„ ๋•Œ (3) โ€” ๋‚จ์€ ์ถœ๊ตฌ๋ฅผ ์ „๋ถ€ ๋‘๋“œ๋ฆฌ๋‹ค: ์˜๋„๋œ sink๋งˆ์ € ๋ง‰ํ˜€ ์žˆ์—ˆ๋‹ค โ€” DreamHack EXP-NaN
blog

[๐Ÿ’Ž Diamond 1] Math.exp๊ฐ€ NaN์„ ์žŠ์„ ๋•Œ (3) โ€” ๋‚จ์€ ์ถœ๊ตฌ๋ฅผ ์ „๋ถ€ ๋‘๋“œ๋ฆฌ๋‹ค: ์˜๋„๋œ sink๋งˆ์ € ๋ง‰ํ˜€ ์žˆ์—ˆ๋‹ค โ€” DreamHack EXP-NaN

2ํŽธ์€ "ํ‘œ์ค€ ๊ฐ’-range ๊ฒฝ๋กœ(CheckBounds ์ œ๊ฑฐ)๋Š” 11.2์—์„œ ๋‹ซํ˜”๋‹ค"๊นŒ์ง€์˜€๋‹ค. 3ํŽธ์€ ๊ทธ ๋’ค โ€” CheckBounds๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š๋Š” *๋น„ํ‘œ์ค€ ์ถœ๊ตฌ*๋ฅผ ํ•˜๋‚˜ํ•˜๋‚˜ ์ „๋ถ€ ๋‘๋“œ๋ฆฐ ๊ธฐ๋ก์ด๋‹ค. fill/copyWithin, foldable ๋นŒํŠธ์ธ, TypedArrayยทDataView, ์š”์†Œ์ข…๋ฅ˜ ์ „์ด, ํ‘œํ˜„ํ˜• ๋ณ€ํ™˜๊นŒ์ง€ ํ›„๋ณด sink๋ฅผ ์ „์ˆ˜๋กœ d8์— ๋•Œ๋ ค๋ดค๊ณ  ์ „๋ถ€ ์ •ํ™”/ํŠธ๋žฉ/in-range๋กœ ์ฃฝ์—ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ฐ€์žฅ ๋œป๋ฐ–์˜ ๋ฐœ๊ฒฌ โ€” ์ด ๋ฒ„๊ทธ๋ฅ˜์˜ *์˜๋„๋œ* ํ•ด๋ฒ•์ธ new Array(len) length-construction๋งˆ์ € ์ด ๋นŒ๋“œ์—์„  ๋ง‰ํ˜€ ์žˆ๋‹ค. ํŒจ์น˜๊ฐ€ typer.cc๋งŒ ๊ฑด๋“œ๋ฆฌ๊ณ  js-create-lowering์˜ ๊ธธ์ด ๋ณดํ˜ธ๋Š” ์•ˆ ํ’€์–ด์„œ๋‹ค. ์–ด๊ธ‹๋‚จ์€ ์‚ด์•„์žˆ๋Š”๋ฐ(์ž์—ฐ ํ‹ฐ์–ด์—…์—์„œ INT_MIN์œผ๋กœ ๋ฐœ์‚ฐ) ๋ฐ›์•„์ค„ sink๊ฐ€ ์—†๋‹ค. ๋์œผ๋กœ ๋‚จ์€ ํ•œ ์ˆ˜ โ€” ๋ฐฐํฌ ๋ฐ”์ด๋„ˆ๋ฆฌ ์ง์ ‘ ๋ถ€๊ฒ€.
#dreamhack#ctf#pwn+7
2026-06-07#dreamhack +9
[๐Ÿ’Ž Diamond 1] Math.exp๊ฐ€ NaN์„ ์žŠ์„ ๋•Œ (5) โ€” read์—์„œ flag๊นŒ์ง€: ๋ถˆ๊ฐ€๋Šฅ์ด๋ผ๋˜ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ์„ ๋’ค์ง‘๊ณ  ๋ผ์ด๋ธŒ ์„œ๋ฒ„๋ฅผ ๋šซ๋‹ค โ€” DreamHack EXP-NaN
blog

[๐Ÿ’Ž Diamond 1] Math.exp๊ฐ€ NaN์„ ์žŠ์„ ๋•Œ (5) โ€” read์—์„œ flag๊นŒ์ง€: ๋ถˆ๊ฐ€๋Šฅ์ด๋ผ๋˜ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ์„ ๋’ค์ง‘๊ณ  ๋ผ์ด๋ธŒ ์„œ๋ฒ„๋ฅผ ๋šซ๋‹ค โ€” DreamHack EXP-NaN

4ํŽธ์€ ".at() ๋ฌด๊ฐ€๋“œ OOB read๋Š” ํ™•์ •, ๊ทธ๋Ÿฐ๋ฐ write๋Š” ์—ฌ์ „ํžˆ ๋ง‰ํ˜€ ์žˆ๊ณ  readโ†’write ๋‹ค๋ฆฌ๋Š” addrof/fakeobj๋ฟ"์œผ๋กœ ๋๋‚ฌ๋‹ค. 5ํŽธ์€ ๊ทธ ๋‹ค๋ฆฌ๋ฅผ ๊ฑด๋„ˆ ๋ผ์ด๋ธŒ flag๊นŒ์ง€ ๊ฐ€๋Š” ๊ธฐ๋ก์ด๋‹ค โ€” ๊ทธ๋ฆฌ๊ณ  ์†”์งํžˆ 3์ผ์ด ๊ฑธ๋ ธ๋‹ค. addrof/fakeobj ๋ถ€ํŠธ์ŠคํŠธ๋žฉ์ด "nursery monoculture๋กœ ๋ถˆ๊ฐ€๋Šฅ"์ด๋ผ๋˜ ๊ฒฐ๋ก ์„ ๋’ค์ง‘๊ณ (์ง„์งœ ์›์ธ์€ globalThis ์ €์žฅ์ด fold๋ฅผ deopt์‹œํ‚จ ๊ฒƒ), fake FixedDoubleArray๋กœ ์ฒซ forward ์ผ€์ด์ง€ R/W๋ฅผ ์—ด๊ณ , ๊ทธ๊ฒŒ GC ์‹ ๋ขฐ์„ฑ์—์„œ ์ดํ‹€์„ ๋ง‰์•˜๋‹ค๊ฐ€ "์˜คํ”„์…‹ ๊ฐ€์ •์„ ๋Ÿฐํƒ€์ž„ leak์œผ๋กœ ๋ฐ”๊พธ๋Š”" ํ•œ ์ค„๋กœ 5/5๊ฐ€ ๋๋‹ค. ๊ฑฐ๊ธฐ์„œ fake Float64Array์˜ base_pointer๋ฅผ ๊ฐˆ์•„๋ผ์›Œ data_ptr์„ ์ ˆ๋Œ€์ฃผ์†Œ๋กœ ์ง€์ •ํ•˜๋Š” ์–‘๋ฐฉํ–ฅ R/W๋กœ backward ์ธ์Šคํ„ด์Šค๊นŒ์ง€ ์ฝ๊ณ , V12 Revenge์—์„œ ์“ด imported_function_targets ๋ฎ๊ธฐ๋กœ ์ ˆ๋Œ€์ฃผ์†Œ ๋ˆ„์ถœ ์—†์ด execve("/bin/sh")๊นŒ์ง€. ์ต์Šค๋Š” ์„œ๋ฒ„ ํ•œ๋„(2000B)์— ๋งž์ถฐ 1999๋ฐ”์ดํŠธ๋กœ ๊นŽ์•˜๊ณ , ๋กœ์ปฌ 10/10์ด ์„œ๋ฒ„์—์„  0/12๋กœ ์ฃฝ๊ธธ๋ž˜ ์ง„๋‹จ์„ ๋ณด๋ƒˆ๋”๋‹ˆ mapArr ํ•˜๋‚˜๋งŒ ํ™˜๊ฒฝ๋งˆ๋‹ค ๋‹ฌ๋ž๋‹ค(0x18d7b9 vs 0x10d7b9) โ€” ๊ทธ๊ฑธ ๋Ÿฐํƒ€์ž„ leak์œผ๋กœ ์ ์‘์‹œ์ผœ host3.dreamhack.games์—์„œ flag๋ฅผ ํšŒ์ˆ˜ํ–ˆ๋‹ค.
#dreamhack#ctf#pwn+8
2026-06-08#dreamhack +8