Merge pull request #8649 from roc-lang/fix-double-decref

Fix double-decref
This commit is contained in:
Richard Feldman 2025-12-12 10:05:53 -05:00 committed by GitHub
commit 9cd58ee4ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 36 additions and 13 deletions

View file

@ -229,6 +229,16 @@ pub const io_spec_tests = [_]TestSpec{
.io_spec = "1>ok",
.description = "Regression test: List.first with function syntax",
},
.{
.roc_file = "test/fx/stdin_while_uaf.roc",
.io_spec = "0<123456789012345678901234|1>123456789012345678901234|0<|1>",
.description = "Regression test: Stdin.line! in while loop with 24 char input (heap-allocated string)",
},
.{
.roc_file = "test/fx/stdin_while_uaf.roc",
.io_spec = "0<short|1>short|0<|1>",
.description = "Regression test: Stdin.line! in while loop with short input (small string optimization)",
},
};
/// Get the total number of IO spec tests

View file

@ -14217,11 +14217,9 @@ pub const Interpreter = struct {
var args = [1]StackValue{operand};
const result = try self.callLowLevelBuiltin(low_level.op, &args, roc_ops, null);
// Decref operand based on ownership semantics
const arg_ownership = low_level.op.getArgOwnership();
if (arg_ownership.len > 0 and arg_ownership[0] == .borrow) {
operand.decref(&self.runtime_layout_store, roc_ops);
}
// Note: We do NOT decref the operand here.
// The defer statement at the top of unary_op_apply already handles decrefing.
// Decrefing here too would cause a double-free bug.
self.env = saved_env;
try value_stack.push(result);
@ -14578,14 +14576,11 @@ pub const Interpreter = struct {
var args = [2]StackValue{ lhs, rhs };
var result = try self.callLowLevelBuiltin(low_level.op, &args, roc_ops, null);
// Decref arguments based on ownership semantics
const arg_ownership = low_level.op.getArgOwnership();
for (args, 0..) |arg, arg_idx| {
const ownership = if (arg_idx < arg_ownership.len) arg_ownership[arg_idx] else .borrow;
if (ownership == .borrow) {
arg.decref(&self.runtime_layout_store, roc_ops);
}
}
// Note: We do NOT decref arguments here for borrow semantics.
// The defer statements at the top of binop_apply already handle decrefing
// lhs and rhs. Decrefing here too would cause a double-free bug.
// For consume semantics, the low-level builtin takes ownership, so we
// also don't decref - the builtin is responsible for the memory.
// Decref the method closure (for low-level, we handle it here)
method_func.decref(&self.runtime_layout_store, roc_ops);

View file

@ -0,0 +1,18 @@
app [main!] { pf: platform "./platform/main.roc" }
import pf.Stdin
import pf.Stdout
main! = || {
# Read lines until empty string
var $count = 0
var $continue = True
while $continue {
line = Stdin.line!()
Stdout.line!(line)
$count = $count + 1
if line == "" {
$continue = False
}
}
}