3 Commits

Author SHA1 Message Date
56e72928c6 fix use after free 2025-05-10 21:46:53 -04:00
a80c9abfe7 Attempt to base64 encode the connection payload
For some reason I am still getting this:

2025/05/10 16:37:06 Error decoding message: SGVsbG8gZGFya25lc3MgbXkgb2xkIGZyaWVuZA==::53475673624738675a4746796132356c63334d6762586b676232786b49475a79615756755a413d3daaaa
2025-05-10 21:46:53 -04:00
245dab4909 Use slice for init, and add better error sets.
The slice sets us avoid allocating within the init function.
This means init can't fail, and it also makes it easier to stack allocate messages (slice an array buffer, instead of creating a stack allocator).
2025-05-10 21:46:53 -04:00
2 changed files with 53 additions and 45 deletions

View File

@@ -48,12 +48,17 @@ fn broadcastSaprusMessage(msg: *SaprusMessage, udp_port: u16) !void {
} }
pub fn sendRelay(payload: []const u8, dest: [4]u8, allocator: Allocator) !void { pub fn sendRelay(payload: []const u8, dest: [4]u8, allocator: Allocator) !void {
const msg: *SaprusMessage = try .init( const msg_bytes = try allocator.alignedAlloc(
allocator, u8,
@alignOf(SaprusMessage),
try SaprusMessage.lengthForPayloadLength(
.relay, .relay,
@intCast(base64Enc.calcSize(payload.len)), base64Enc.calcSize(payload.len),
),
); );
defer msg.deinit(allocator); defer allocator.free(msg_bytes);
const msg: *SaprusMessage = .init(.relay, msg_bytes);
const relay = (try msg.getSaprusTypePayload()).relay; const relay = (try msg.getSaprusTypePayload()).relay;
relay.dest = dest; relay.dest = dest;
_ = base64Enc.encode(relay.getPayload(), payload); _ = base64Enc.encode(relay.getPayload(), payload);
@@ -72,12 +77,21 @@ fn randomPort() u16 {
pub fn sendInitialConnection(payload: []const u8, initial_port: u16, allocator: Allocator) !*SaprusMessage { pub fn sendInitialConnection(payload: []const u8, initial_port: u16, allocator: Allocator) !*SaprusMessage {
const dest_port = randomPort(); const dest_port = randomPort();
const msg: *SaprusMessage = try .init(allocator, .connection, @intCast(payload.len)); const msg_bytes = try allocator.alignedAlloc(
defer msg.deinit(allocator); u8,
@alignOf(SaprusMessage),
try SaprusMessage.lengthForPayloadLength(
.connection,
base64Enc.calcSize(payload.len),
),
);
const msg: *SaprusMessage = .init(.connection, msg_bytes);
const connection = (try msg.getSaprusTypePayload()).connection; const connection = (try msg.getSaprusTypePayload()).connection;
connection.src_port = initial_port; connection.src_port = initial_port;
connection.dest_port = dest_port; connection.dest_port = dest_port;
@memcpy(connection.getPayload(), payload); _ = base64Enc.encode(connection.getPayload(), payload);
try broadcastSaprusMessage(msg, 8888); try broadcastSaprusMessage(msg, 8888);
@@ -90,8 +104,7 @@ pub fn connect(payload: []const u8, allocator: Allocator) !?SaprusConnection {
initial_port = r.intRangeAtMost(u16, 1024, 65000); initial_port = r.intRangeAtMost(u16, 1024, 65000);
} else unreachable; } else unreachable;
var initial_conn_res: ?SaprusMessage = null; var initial_conn_res: ?*SaprusMessage = null;
errdefer if (initial_conn_res) |*c| c.deinit(allocator);
var sock = try network.Socket.create(.ipv4, .udp); var sock = try network.Socket.create(.ipv4, .udp);
defer sock.close(); defer sock.close();
@@ -107,13 +120,14 @@ pub fn connect(payload: []const u8, allocator: Allocator) !?SaprusConnection {
try sock.bind(bind_addr); try sock.bind(bind_addr);
const msg = try sendInitialConnection(payload, initial_port, allocator); const msg = try sendInitialConnection(payload, initial_port, allocator);
defer allocator.free(msg.asBytes());
var response_buf: [4096]u8 align(4) = @splat(0); var response_buf: [4096]u8 align(@alignOf(SaprusMessage)) = undefined;
_ = try sock.receive(&response_buf); // Ignore message that I sent. _ = try sock.receive(&response_buf); // Ignore message that I sent.
const len = try sock.receive(&response_buf); const len = try sock.receive(&response_buf);
std.debug.print("response bytes: {x}\n", .{response_buf}); std.debug.print("response bytes: {x}\n", .{response_buf[0..len]});
initial_conn_res = (try SaprusMessage.bytesAsValue(response_buf[0..len])).*; initial_conn_res = SaprusMessage.init(.connection, response_buf[0..len]);
// Complete handshake after awaiting response // Complete handshake after awaiting response
try broadcastSaprusMessage(msg, randomPort()); try broadcastSaprusMessage(msg, randomPort());

View File

@@ -20,9 +20,11 @@ pub const ConnectionOptions = packed struct(u8) {
opt8: bool = false, opt8: bool = false,
}; };
pub const Error = error{ pub const MessageTypeError = error{
NotImplementedSaprusType, NotImplementedSaprusType,
UnknownSaprusType, UnknownSaprusType,
};
pub const MessageParseError = MessageTypeError || error{
InvalidMessage, InvalidMessage,
}; };
@@ -75,23 +77,25 @@ pub const Message = packed struct {
length: u16, length: u16,
bytes: void = {}, bytes: void = {},
pub fn init(allocator: Allocator, comptime @"type": PacketType, payload_len: u16) !*Self { /// Takes a byte slice, and returns a Message struct backed by the slice.
const header_size = @sizeOf(switch (@"type") { /// This properly initializes the top level headers within the slice.
.relay => Relay, pub fn init(@"type": PacketType, bytes: []align(@alignOf(Self)) u8) *Self {
.connection => Connection, std.debug.assert(bytes.len >= @sizeOf(Self));
.file_transfer => return Error.NotImplementedSaprusType,
else => return Error.UnknownSaprusType,
});
const size = payload_len + @sizeOf(Self) + header_size;
const bytes = try allocator.alignedAlloc(u8, @alignOf(Self), size);
const res: *Self = @ptrCast(bytes.ptr); const res: *Self = @ptrCast(bytes.ptr);
res.type = @"type"; res.type = @"type";
res.length = payload_len + header_size; res.length = @intCast(bytes.len - @sizeOf(Self));
return res; return res;
} }
pub fn deinit(self: *Self, allocator: Allocator) void { pub fn lengthForPayloadLength(comptime @"type": PacketType, payload_len: usize) MessageTypeError!u16 {
allocator.free(self.asBytes()); std.debug.assert(payload_len < std.math.maxInt(u16));
const header_size = @sizeOf(switch (@"type") {
.relay => Relay,
.connection => Connection,
.file_transfer => return MessageTypeError.NotImplementedSaprusType,
else => return MessageTypeError.UnknownSaprusType,
});
return @intCast(payload_len + @sizeOf(Self) + header_size);
} }
fn getRelay(self: *Self) *align(1) Relay { fn getRelay(self: *Self) *align(1) Relay {
@@ -101,7 +105,7 @@ pub const Message = packed struct {
return std.mem.bytesAsValue(Connection, &self.bytes); return std.mem.bytesAsValue(Connection, &self.bytes);
} }
pub fn getSaprusTypePayload(self: *Self) Error!(union(PacketType) { pub fn getSaprusTypePayload(self: *Self) MessageTypeError!(union(PacketType) {
relay: *align(1) Relay, relay: *align(1) Relay,
file_transfer: void, file_transfer: void,
connection: *align(1) Connection, connection: *align(1) Connection,
@@ -109,12 +113,12 @@ pub const Message = packed struct {
return switch (self.type) { return switch (self.type) {
.relay => .{ .relay = self.getRelay() }, .relay => .{ .relay = self.getRelay() },
.connection => .{ .connection = self.getConnection() }, .connection => .{ .connection = self.getConnection() },
.file_transfer => Error.NotImplementedSaprusType, .file_transfer => MessageTypeError.NotImplementedSaprusType,
else => Error.UnknownSaprusType, else => MessageTypeError.UnknownSaprusType,
}; };
} }
pub fn nativeFromNetworkEndian(self: *Self) Error!void { pub fn nativeFromNetworkEndian(self: *Self) MessageTypeError!void {
self.type = @enumFromInt(bigToNative( self.type = @enumFromInt(bigToNative(
@typeInfo(@TypeOf(self.type)).@"enum".tag_type, @typeInfo(@TypeOf(self.type)).@"enum".tag_type,
@intFromEnum(self.type), @intFromEnum(self.type),
@@ -137,12 +141,12 @@ pub const Message = packed struct {
} }
} }
pub fn networkFromNativeEndian(self: *Self) Error!void { pub fn networkFromNativeEndian(self: *Self) MessageTypeError!void {
try switch (try self.getSaprusTypePayload()) { try switch (try self.getSaprusTypePayload()) {
.relay => {}, .relay => {},
.connection => |*con| con.*.networkFromNativeEndian(), .connection => |*con| con.*.networkFromNativeEndian(),
.file_transfer => Error.NotImplementedSaprusType, .file_transfer => MessageTypeError.NotImplementedSaprusType,
else => Error.UnknownSaprusType, else => MessageTypeError.UnknownSaprusType,
}; };
self.type = @enumFromInt(nativeToBig( self.type = @enumFromInt(nativeToBig(
@typeInfo(@TypeOf(self.type)).@"enum".tag_type, @typeInfo(@TypeOf(self.type)).@"enum".tag_type,
@@ -151,18 +155,8 @@ pub const Message = packed struct {
self.length = nativeToBig(@TypeOf(self.length), self.length); self.length = nativeToBig(@TypeOf(self.length), self.length);
} }
pub fn bytesAsValue(bytes: SelfBytes) !*Self { /// Deprecated.
const res = std.mem.bytesAsValue(Self, bytes); /// If I need the bytes, I should just pass around the slice that is backing this to begin with.
return switch (res.type) {
.relay, .connection => if (bytes.len == res.length + @sizeOf(Self))
res
else
Error.InvalidMessage,
.file_transfer => Error.NotImplementedSaprusType,
else => Error.UnknownSaprusType,
};
}
pub fn asBytes(self: *Self) SelfBytes { pub fn asBytes(self: *Self) SelfBytes {
const size = @sizeOf(Self) + self.length; const size = @sizeOf(Self) + self.length;
return @as([*]align(@alignOf(Self)) u8, @ptrCast(self))[0..size]; return @as([*]align(@alignOf(Self)) u8, @ptrCast(self))[0..size];