Search code examples
zig

Memory Leak in randomString function


Cannot fix memory leak in this code. Can you help me please?

The function generates a simple string by the length specified in the argument, using the allocator passed through the arguments

const std = @import("std");

pub fn randomString(allocator: std.mem.Allocator, size: u32, charset: []const u8) ![]const u8 {
    if (size == 0) {
        return error.InvalidSize;
    }
    if (charset.len == 0) {
        return error.InvalidCharset;
    }

    const buffer = try allocator.alloc(u8, size);
    defer allocator.free(buffer);

    var prng = std.rand.DefaultPrng.init(blk: {
        var seed: u64 = undefined;
        try std.posix.getrandom(std.mem.asBytes(&seed));
        break :blk seed;
    });

    var rand = prng.random();

    for (buffer) |*ch| {
        const idx = rand.int(u64) % charset.len;
        ch.* = charset[idx];
    }

    const result = try allocator.dupe(u8, buffer);

    return result;
}

As far as I understand from the logs, the leak occurs after allocator.dupe in the code, but I try to remove it, then the memory leak occurs in the buffer variable.

Test logs:

Test [1/1] string.test.should generate random string with specified charset... [gpa] (err): memory address 0x102584010 leaked:
/opt/homebrew/Cellar/zig/0.12.0/lib/zig/std/mem/Allocator.zig:319:40: 0x1024794a3 in dupe__anon_3182 (test)
    const new_buf = try allocator.alloc(T, m.len);
                                       ^
/Users/kitanoyoru/Code/fun/zig/z/src/string.zig:27:38: 0x102478a77 in randomString (test)
    const result = try allocator.dupe(u8, buffer);
                                     ^
/Users/kitanoyoru/Code/fun/zig/z/src/string.zig:41:32: 0x1024796f7 in test.should generate random string with specified charset (test)
    const result = randomString(allocator, size, charset) catch |err| {

All 1 tests passed.
1 errors were logged.
error: the following test command failed with exit code 1:
/Users/kitanoyoru/Code/fun/zig/z/zig-cache/o/497544b27d5ac9c0b1837d7a0bed4b2b/test

Solution

  • Do you free what randomString returns? It must be freed:

    test "..." {
        const allocator = ...
        const random = try randomString(allocator, ...);
        defer allocator.free(random);
    
        // ...
    }
    

    Also, you don't need to duplicate the buffer, you can just return it:

    pub fn randomString(allocator: std.mem.Allocator, size: u32, charset: []const u8) ![]u8 {
        if (size == 0) {
            return error.InvalidSize;
        }
        if (charset.len == 0) {
            return error.InvalidCharset;
        }
    
        var prng = std.rand.DefaultPrng.init(blk: {
            var seed: u64 = undefined;
            try std.posix.getrandom(std.mem.asBytes(&seed));
            break :blk seed;
        });
        var rand = prng.random();
    
        const result = try allocator.alloc(u8, size);
        for (result) |*ch| {
            const idx = rand.int(u64) % charset.len;
            ch.* = charset[idx];
        }
        return result;
    }