Zig NEWS

Pyrolistical
Pyrolistical

Posted on

Puzzler: Pass null anyways

Here is another puzzler. Let's say we have an extern,

extern fn foo(name: [*:0]const u8) callconv(.c) void;
Enter fullscreen mode Exit fullscreen mode

But a mistake was made, and the actual underlying library accepts a null for name.

While the correct thing to do is to fix the extern signature with name: ?[*:0]const u8, let's says we are not in control of that.

What are all the ways we can pass null anyways?

Multiple answers, select all that apply:

  1. foo(null);
  2. foo(@bitCast(null));
  3. foo(@ptrFromInt(0));
  4. foo(@ptrCast(@as(?[*:0]const u8, null)));
  5. foo(@ptrCast(@as([*:0]allowzero const u8, @ptrFromInt(0))));
  6. foo(@allowzeroCast(@as([*:0]allowzero const u8, @ptrFromInt(0))));
  7. foo(null: {
      var ptr: ?[*:0]const u8 = null;
      _ = &ptr;
      break :null @ptrFromInt(@intFromPtr(ptr));
    });
    
  8. None of the above

Answer is after the whale.

whale

Let's go through the options one at time.

A. foo(null);

Compiler error:

error: expected type '[*:0]const u8', found '@TypeOf(null)'
Enter fullscreen mode Exit fullscreen mode

B. foo(@bitCast(null));

Compiler error:

error: cannot @bitCast to '[*:0]const u8'
Enter fullscreen mode Exit fullscreen mode

C. foo(@ptrFromInt(0));

Compiler error:

error: pointer type '[*:0]const u8' does not allow address zero
Enter fullscreen mode Exit fullscreen mode

D. foo(@ptrCast(@as(?[*:0]const u8, null)));

Compiler error:

error: null pointer casted to type '[*:0]const u8'
Enter fullscreen mode Exit fullscreen mode

E. foo(@ptrCast(@as([*:0]allowzero const u8, @ptrFromInt(0))));

Compiler error:

error: null pointer casted to type '[*:0]const u8'
Enter fullscreen mode Exit fullscreen mode

F. foo(@allowzeroCast(@as([*:0]allowzero const u8, @ptrFromInt(0))));

Compiler error:

error: invalid builtin function: '@allowzeroCast'
Enter fullscreen mode Exit fullscreen mode

G.

foo(null: {
  var ptr: ?[*:0]const u8 = null;
  _ = &ptr;
  break :null @ptrFromInt(@intFromPtr(ptr));
});

This one actually compiles! But then panics at runtime:

panic: cast causes pointer to be null
Enter fullscreen mode Exit fullscreen mode

H. None of the above

AFAIK, the Zig compiler/runtime is too smart to be tricked. The only way is to fix the extern signature.

Motivating issue: https://github.com/ziglang/zig/issues/19946

Top comments (1)

Collapse
 
jrfondren profile image
jrfondren

Of course you can always lie...

extern fn foo(name: [*:0]const u8) callconv(.C) void;
extern fn cheater(name: [*:0]allowzero const u8) callconv(.C) [*:0]const u8;

test "pass null anyways" {
    foo(cheater(@ptrFromInt(0)));
}
Enter fullscreen mode Exit fullscreen mode

with cheater.c:

#include <stdio.h>

void foo(char *name) {
    printf("foo(\"%s\")\n", name);
}

char* cheater(char *name) {
    return name;
}
Enter fullscreen mode Exit fullscreen mode