Skip to content

Double free on duplicate map key #111

@lepton9

Description

@lepton9

A double free occurs when trying to add an entry with a duplicate key.

Error is shown by following test:

test "double_free" {
    const source =
        \\ name: test
        \\ jobs:
        \\   name:
        \\     steps: []
        \\   name:
        \\     steps: []
    ;

    const gpa = std.testing.allocator;
    var yaml_parser: yaml.Yaml = .{ .source = source };
    defer yaml_parser.deinit(gpa);
    try yaml_parser.load(gpa);
}

Error is:

$ zig build test
test
└─ run test failure
[gpa] (err): Double free detected. Allocation:
/usr/lib/zig/std/mem/Allocator.zig:436:40: 0x1083e8e in dupe__anon_5587 (std.zig)
    const new_buf = try allocator.alloc(T, m.len);
                                       ^
/home/lepton/projects/zig-yaml/src/Yaml.zig:496:45: 0x10595c7 in fromNode (lib.zig)
                    const key = try gpa.dupe(u8, tree.rawString(entry.data.key, entry.data.key));
                                            ^
/home/lepton/projects/zig-yaml/src/Yaml.zig:447:38: 0x1058423 in fromNode (lib.zig)
                return Value.fromNode(gpa, tree, inner);
                                     ^
/home/lepton/projects/zig-yaml/src/Yaml.zig:52:41: 0x1050577 in load (lib.zig)
        const value = try Value.fromNode(gpa, self.tree.?, node);
                                        ^
/home/lepton/projects/demo/src/main.zig:33:25: 0x1050b80 in test.double_free (main.zig)
    try yaml_parser.load(gpa);
                        ^
/usr/lib/zig/compiler/test_runner.zig:130:29: 0x11d507e in mainServer (test_runner.zig)
                test_fn.func() catch |err| switch (err) {
                            ^
/usr/lib/zig/compiler/test_runner.zig:64:26: 0x11d6336 in main (test_runner.zig)
        return mainServer() catch @panic("internal test runner failure");
                         ^
/usr/lib/zig/std/start.zig:618:22: 0x11d011d in posixCallMainAndExit (std.zig)
            root.main();
                     ^
/usr/lib/zig/std/start.zig:232:5: 0x11cf9b1 in _start (std.zig)
    asm volatile (switch (native_arch) {
    ^
 First free:
/home/lepton/projects/zig-yaml/src/Yaml.zig:497:38: 0x1059e10 in fromNode (lib.zig)
                    errdefer gpa.free(key);
                                     ^
/home/lepton/projects/zig-yaml/src/Yaml.zig:447:38: 0x1058423 in fromNode (lib.zig)
                return Value.fromNode(gpa, tree, inner);
                                     ^
/home/lepton/projects/zig-yaml/src/Yaml.zig:52:41: 0x1050577 in load (lib.zig)
        const value = try Value.fromNode(gpa, self.tree.?, node);
                                        ^
/home/lepton/projects/demo/src/main.zig:33:25: 0x1050b80 in test.double_free (main.zig)
    try yaml_parser.load(gpa);
                        ^
/usr/lib/zig/compiler/test_runner.zig:130:29: 0x11d507e in mainServer (test_runner.zig)
                test_fn.func() catch |err| switch (err) {
                            ^
/usr/lib/zig/compiler/test_runner.zig:64:26: 0x11d6336 in main (test_runner.zig)
        return mainServer() catch @panic("internal test runner failure");
                         ^
/usr/lib/zig/std/start.zig:618:22: 0x11d011d in posixCallMainAndExit (std.zig)
            root.main();
                     ^
/usr/lib/zig/std/start.zig:232:5: 0x11cf9b1 in _start (std.zig)
    asm volatile (switch (native_arch) {
    ^
 Second free:
/home/lepton/projects/zig-yaml/src/Yaml.zig:484:33: 0x105a024 in fromNode (lib.zig)
                        gpa.free(key);
                                ^
/home/lepton/projects/zig-yaml/src/Yaml.zig:447:38: 0x1058423 in fromNode (lib.zig)
                return Value.fromNode(gpa, tree, inner);
                                     ^
/home/lepton/projects/zig-yaml/src/Yaml.zig:52:41: 0x1050577 in load (lib.zig)
        const value = try Value.fromNode(gpa, self.tree.?, node);
                                        ^
/home/lepton/projects/demo/src/main.zig:33:25: 0x1050b80 in test.double_free (main.zig)
    try yaml_parser.load(gpa);
                        ^
/usr/lib/zig/compiler/test_runner.zig:130:29: 0x11d507e in mainServer (test_runner.zig)
                test_fn.func() catch |err| switch (err) {
                            ^
/usr/lib/zig/compiler/test_runner.zig:64:26: 0x11d6336 in main (test_runner.zig)
        return mainServer() catch @panic("internal test runner failure");
                         ^
/usr/lib/zig/std/start.zig:618:22: 0x11d011d in posixCallMainAndExit (std.zig)
            root.main();
                     ^
/usr/lib/zig/std/start.zig:232:5: 0x11cf9b1 in _start (std.zig)
    asm volatile (switch (native_arch) {
    ^

thread 338456 panic: switch on corrupt value
/home/lepton/projects/zig-yaml/src/Yaml.zig:500:45: 0x1059cda in fromNode (lib.zig)
                    if (gop.found_existing) return error.DuplicateMapKey;
                                            ^
/home/lepton/projects/zig-yaml/src/Yaml.zig:324:17: 0x10853a6 in deinit (lib.zig)
        switch (self.*) {
                ^
/home/lepton/projects/zig-yaml/src/Yaml.zig:485:37: 0x105a04b in fromNode (lib.zig)
                        value.deinit(gpa);
                                    ^
/home/lepton/projects/zig-yaml/src/Yaml.zig:447:38: 0x1058423 in fromNode (lib.zig)
                return Value.fromNode(gpa, tree, inner);
                                     ^
/home/lepton/projects/zig-yaml/src/Yaml.zig:52:41: 0x1050577 in load (lib.zig)
        const value = try Value.fromNode(gpa, self.tree.?, node);
                                        ^
/home/lepton/projects/demo/src/main.zig:33:25: 0x1050b80 in test.double_free (main.zig)
    try yaml_parser.load(gpa);
                        ^
/usr/lib/zig/compiler/test_runner.zig:130:29: 0x11d507e in mainServer (test_runner.zig)
                test_fn.func() catch |err| switch (err) {
                            ^
/usr/lib/zig/compiler/test_runner.zig:64:26: 0x11d6336 in main (test_runner.zig)
        return mainServer() catch @panic("internal test runner failure");
                         ^
/usr/lib/zig/std/start.zig:618:22: 0x11d011d in posixCallMainAndExit (std.zig)
            root.main();
                     ^
/usr/lib/zig/std/start.zig:232:5: 0x11cf9b1 in _start (std.zig)
    asm volatile (switch (native_arch) {
    ^
???:?:?: 0x0 in ??? (???)
error: while executing test 'main.test.double_free', the following command terminated with signal 6 (expected exited with code 0):
./.zig-cache/o/e0aaec5b41d88640ff6352b7dc4cb6db/test --cache-dir=./.zig-cache --seed=0xfdf10dcd --listen=-

Build Summary: 1/3 steps succeeded; 1 failed; 1/1 tests passed
test transitive failure
└─ run test failure

error: the following build command failed with exit code 1:
.zig-cache/o/ac9a1f8922d4d3a8fcae40d86754c5f2/build /usr/bin/zig /usr/lib/zig /home/lepton/projects/demo .zig-cache /home/lepton/.cache/zig --seed 0xfdf10dcd -Zd315e20251cfeeba test

It appears to happen because the following errdefer frees the key:

errdefer gpa.free(key);

Then, when freeing the whole map, it tries to free the already freed key.
It also tries to free the entry value, but there is no value because it returned the DuplicateMapKey error, thus panics.

zig-yaml/src/Yaml.zig

Lines 482 to 488 in a6c2cd8

errdefer {
for (out_map.keys(), out_map.values()) |key, *value| {
gpa.free(key);
value.deinit(gpa);
}
out_map.deinit(gpa);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions