authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-25 15:14:28-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-26 01:01:16-08:00
log032c2ee9bc1fae507be776c118cd03d28c865edc
tree4e777b7e02d293cb05b78000052fed18982f43d5
parent723d13f8314a88f75a1068db7408677d8cc0c165

std.http.Client: fix UAF when handling redirects

closes #19071

2 files changed, 79 insertions(+), 2 deletions(-)

lib/std/http/Client.zig+5-2
......@@ -857,9 +857,12 @@ pub const Request = struct {
857857 /// Must be called after `send` and, if any data was written to the request
858858 /// body, then also after `finish`.
859859 pub fn wait(req: *Request) WaitError!void {
860 const connection = req.connection.?;
860 while (true) {
861 // This while loop is for handling redirects, which means the request's
862 // connection may be different than the previous iteration. However, it
863 // is still guaranteed to be non-null with each iteration of this loop.
864 const connection = req.connection.?;
861865
862 while (true) { // handle redirects
863866 while (true) { // read headers
864867 try connection.fill();
865868
lib/std/http/test.zig+74
......@@ -1063,3 +1063,77 @@ fn createTestServer(S: type) !*TestServer {
10631063 test_server.server_thread = try std.Thread.spawn(.{}, S.run, .{&test_server.net_server});
10641064 return test_server;
10651065}
1066
1067test "redirect to different connection" {
1068 const test_server_new = try createTestServer(struct {
1069 fn run(net_server: *std.net.Server) anyerror!void {
1070 var header_buffer: [888]u8 = undefined;
1071
1072 const conn = try net_server.accept();
1073 defer conn.stream.close();
1074
1075 var server = http.Server.init(conn, &header_buffer);
1076 var request = try server.receiveHead();
1077 try expectEqualStrings(request.head.target, "/ok");
1078 try request.respond("good job, you pass", .{});
1079 }
1080 });
1081 defer test_server_new.destroy();
1082
1083 const global = struct {
1084 var other_port: ?u16 = null;
1085 };
1086 global.other_port = test_server_new.port();
1087
1088 const test_server_orig = try createTestServer(struct {
1089 fn run(net_server: *std.net.Server) anyerror!void {
1090 var header_buffer: [999]u8 = undefined;
1091 var send_buffer: [100]u8 = undefined;
1092
1093 const conn = try net_server.accept();
1094 defer conn.stream.close();
1095
1096 const new_loc = try std.fmt.bufPrint(&send_buffer, "http://127.0.0.1:{d}/ok", .{
1097 global.other_port.?,
1098 });
1099
1100 var server = http.Server.init(conn, &header_buffer);
1101 var request = try server.receiveHead();
1102 try expectEqualStrings(request.head.target, "/help");
1103 try request.respond("", .{
1104 .status = .found,
1105 .extra_headers = &.{
1106 .{ .name = "location", .value = new_loc },
1107 },
1108 });
1109 }
1110 });
1111 defer test_server_orig.destroy();
1112
1113 const gpa = std.testing.allocator;
1114
1115 var client: http.Client = .{ .allocator = gpa };
1116 defer client.deinit();
1117
1118 var loc_buf: [100]u8 = undefined;
1119 const location = try std.fmt.bufPrint(&loc_buf, "http://127.0.0.1:{d}/help", .{
1120 test_server_orig.port(),
1121 });
1122 const uri = try std.Uri.parse(location);
1123
1124 {
1125 var server_header_buffer: [666]u8 = undefined;
1126 var req = try client.open(.GET, uri, .{
1127 .server_header_buffer = &server_header_buffer,
1128 });
1129 defer req.deinit();
1130
1131 try req.send(.{});
1132 try req.wait();
1133
1134 const body = try req.reader().readAllAlloc(gpa, 8192);
1135 defer gpa.free(body);
1136
1137 try expectEqualStrings("good job, you pass", body);
1138 }
1139}