Skip to content

Commit 3b26572

Browse files
authored
refactor(ffi): check pointer arguments for NULL (#2624)
This changes all the extern C functions in `hyper::ffi` to check passed pointer arguments for being `NULL` before trying to use them. Before, we would just assume the programmer had passed a good pointer, which could result in segmentation faults. Now: - In debug builds, it will assert they aren't null, and so if they are, a message identifying the argument name will be printed and then the process will crash. - In release builds, it will still check for null, but if found, it will return early, with a return value indicating failure if the return type allows (such as returning NULL, or `HYPERE_INVALID_ARG`). Closes #2620
1 parent c351539 commit 3b26572

File tree

8 files changed

+85
-93
lines changed

8 files changed

+85
-93
lines changed

capi/examples/upload.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,10 @@ static void print_informational(void *userdata, const hyper_response *resp) {
153153

154154
printf("\nInformational (1xx): %d\n", http_status);
155155

156-
const hyper_buf* headers = hyper_response_headers_raw(resp);
157-
write(1, hyper_buf_bytes(headers), hyper_buf_len(headers));
156+
const hyper_buf *headers = hyper_response_headers_raw(resp);
157+
if (headers) {
158+
write(1, hyper_buf_bytes(headers), hyper_buf_len(headers));
159+
}
158160
}
159161

160162
typedef enum {

src/ffi/body.rs

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,7 @@ ffi_fn! {
4040
ffi_fn! {
4141
/// Free a `hyper_body *`.
4242
fn hyper_body_free(body: *mut hyper_body) {
43-
if body.is_null() {
44-
return;
45-
}
46-
47-
drop(unsafe { Box::from_raw(body) });
43+
drop(non_null!(Box::from_raw(body) ?= ()));
4844
}
4945
}
5046

@@ -61,7 +57,7 @@ ffi_fn! {
6157
/// However, it MUST NOT be used or freed until the related task completes.
6258
fn hyper_body_data(body: *mut hyper_body) -> *mut hyper_task {
6359
// This doesn't take ownership of the Body, so don't allow destructor
64-
let mut body = ManuallyDrop::new(unsafe { Box::from_raw(body) });
60+
let mut body = ManuallyDrop::new(non_null!(Box::from_raw(body) ?= ptr::null_mut()));
6561

6662
Box::into_raw(hyper_task::boxed(async move {
6763
body.0.data().await.map(|res| res.map(hyper_buf))
@@ -81,11 +77,7 @@ ffi_fn! {
8177
///
8278
/// This will consume the `hyper_body *`, you shouldn't use it anymore or free it.
8379
fn hyper_body_foreach(body: *mut hyper_body, func: hyper_body_foreach_callback, userdata: *mut c_void) -> *mut hyper_task {
84-
if body.is_null() {
85-
return ptr::null_mut();
86-
}
87-
88-
let mut body = unsafe { Box::from_raw(body) };
80+
let mut body = non_null!(Box::from_raw(body) ?= ptr::null_mut());
8981
let userdata = UserDataPointer(userdata);
9082

9183
Box::into_raw(hyper_task::boxed(async move {
@@ -103,7 +95,7 @@ ffi_fn! {
10395
ffi_fn! {
10496
/// Set userdata on this body, which will be passed to callback functions.
10597
fn hyper_body_set_userdata(body: *mut hyper_body, userdata: *mut c_void) {
106-
let b = unsafe { &mut *body };
98+
let b = non_null!(&mut *body ?= ());
10799
b.0.as_ffi_mut().userdata = userdata;
108100
}
109101
}
@@ -129,7 +121,7 @@ ffi_fn! {
129121
/// If some error has occurred, you can return `HYPER_POLL_ERROR` to abort
130122
/// the body.
131123
fn hyper_body_set_data_func(body: *mut hyper_body, func: hyper_body_data_callback) {
132-
let b = unsafe { &mut *body };
124+
let b = non_null!{ &mut *body ?= () };
133125
b.0.as_ffi_mut().data_func = func;
134126
}
135127
}

src/ffi/client.rs

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::ptr;
12
use std::sync::Arc;
23

34
use libc::c_int;
@@ -37,15 +38,8 @@ ffi_fn! {
3738
/// The returned `hyper_task *` must be polled with an executor until the
3839
/// handshake completes, at which point the value can be taken.
3940
fn hyper_clientconn_handshake(io: *mut hyper_io, options: *mut hyper_clientconn_options) -> *mut hyper_task {
40-
if io.is_null() {
41-
return std::ptr::null_mut();
42-
}
43-
if options.is_null() {
44-
return std::ptr::null_mut();
45-
}
46-
47-
let options = unsafe { Box::from_raw(options) };
48-
let io = unsafe { Box::from_raw(io) };
41+
let options = non_null! { Box::from_raw(options) ?= ptr::null_mut() };
42+
let io = non_null! { Box::from_raw(io) ?= ptr::null_mut() };
4943

5044
Box::into_raw(hyper_task::boxed(async move {
5145
options.builder.handshake::<_, crate::Body>(io)
@@ -66,19 +60,12 @@ ffi_fn! {
6660
/// Returns a task that needs to be polled until it is ready. When ready, the
6761
/// task yields a `hyper_response *`.
6862
fn hyper_clientconn_send(conn: *mut hyper_clientconn, req: *mut hyper_request) -> *mut hyper_task {
69-
if conn.is_null() {
70-
return std::ptr::null_mut();
71-
}
72-
if req.is_null() {
73-
return std::ptr::null_mut();
74-
}
75-
76-
let mut req = unsafe { Box::from_raw(req) };
63+
let mut req = non_null! { Box::from_raw(req) ?= ptr::null_mut() };
7764

7865
// Update request with original-case map of headers
7966
req.finalize_request();
8067

81-
let fut = unsafe { &mut *conn }.tx.send_request(req.0);
68+
let fut = non_null! { &mut *conn ?= ptr::null_mut() }.tx.send_request(req.0);
8269

8370
let fut = async move {
8471
fut.await.map(hyper_response::wrap)
@@ -91,7 +78,7 @@ ffi_fn! {
9178
ffi_fn! {
9279
/// Free a `hyper_clientconn *`.
9380
fn hyper_clientconn_free(conn: *mut hyper_clientconn) {
94-
drop(unsafe { Box::from_raw(conn) });
81+
drop(non_null! { Box::from_raw(conn) ?= () });
9582
}
9683
}
9784

@@ -119,7 +106,7 @@ ffi_fn! {
119106
ffi_fn! {
120107
/// Free a `hyper_clientconn_options *`.
121108
fn hyper_clientconn_options_free(opts: *mut hyper_clientconn_options) {
122-
drop(unsafe { Box::from_raw(opts) });
109+
drop(non_null! { Box::from_raw(opts) ?= () });
123110
}
124111
}
125112

@@ -128,9 +115,9 @@ ffi_fn! {
128115
///
129116
/// This does not consume the `options` or the `exec`.
130117
fn hyper_clientconn_options_exec(opts: *mut hyper_clientconn_options, exec: *const hyper_executor) {
131-
let opts = unsafe { &mut *opts };
118+
let opts = non_null! { &mut *opts ?= () };
132119

133-
let exec = unsafe { Arc::from_raw(exec) };
120+
let exec = non_null! { Arc::from_raw(exec) ?= () };
134121
let weak_exec = hyper_executor::downgrade(&exec);
135122
std::mem::forget(exec);
136123

@@ -146,7 +133,7 @@ ffi_fn! {
146133
fn hyper_clientconn_options_http2(opts: *mut hyper_clientconn_options, enabled: c_int) -> hyper_code {
147134
#[cfg(feature = "http2")]
148135
{
149-
let opts = unsafe { &mut *opts };
136+
let opts = non_null! { &mut *opts ?= hyper_code::HYPERE_INVALID_ARG };
150137
opts.builder.http2_only(enabled != 0);
151138
hyper_code::HYPERE_OK
152139
}
@@ -168,7 +155,7 @@ ffi_fn! {
168155
///
169156
/// If enabled, see `hyper_response_headers_raw()` for usage.
170157
fn hyper_clientconn_options_headers_raw(opts: *mut hyper_clientconn_options, enabled: c_int) -> hyper_code {
171-
let opts = unsafe { &mut *opts };
158+
let opts = non_null! { &mut *opts ?= hyper_code::HYPERE_INVALID_ARG };
172159
opts.builder.http1_headers_raw(enabled != 0);
173160
hyper_code::HYPERE_OK
174161
}

src/ffi/error.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,14 @@ impl hyper_error {
5858
ffi_fn! {
5959
/// Frees a `hyper_error`.
6060
fn hyper_error_free(err: *mut hyper_error) {
61-
drop(unsafe { Box::from_raw(err) });
61+
drop(non_null!(Box::from_raw(err) ?= ()));
6262
}
6363
}
6464

6565
ffi_fn! {
6666
/// Get an equivalent `hyper_code` from this error.
6767
fn hyper_error_code(err: *const hyper_error) -> hyper_code {
68-
unsafe { &*err }.code()
68+
non_null!(&*err ?= hyper_code::HYPERE_INVALID_ARG).code()
6969
}
7070
}
7171

@@ -80,6 +80,6 @@ ffi_fn! {
8080
let dst = unsafe {
8181
std::slice::from_raw_parts_mut(dst, dst_len)
8282
};
83-
unsafe { &*err }.print_to(dst)
83+
non_null!(&*err ?= 0).print_to(dst)
8484
}
8585
}

src/ffi/http_types.rs

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ ffi_fn! {
4848
ffi_fn! {
4949
/// Free an HTTP request if not going to send it on a client.
5050
fn hyper_request_free(req: *mut hyper_request) {
51-
drop(unsafe { Box::from_raw(req) });
51+
drop(non_null!(Box::from_raw(req) ?= ()));
5252
}
5353
}
5454

@@ -58,9 +58,10 @@ ffi_fn! {
5858
let bytes = unsafe {
5959
std::slice::from_raw_parts(method, method_len as usize)
6060
};
61+
let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG);
6162
match Method::from_bytes(bytes) {
6263
Ok(m) => {
63-
*unsafe { &mut *req }.0.method_mut() = m;
64+
*req.0.method_mut() = m;
6465
hyper_code::HYPERE_OK
6566
},
6667
Err(_) => {
@@ -76,9 +77,10 @@ ffi_fn! {
7677
let bytes = unsafe {
7778
std::slice::from_raw_parts(uri, uri_len as usize)
7879
};
80+
let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG);
7981
match Uri::from_maybe_shared(bytes) {
8082
Ok(u) => {
81-
*unsafe { &mut *req }.0.uri_mut() = u;
83+
*req.0.uri_mut() = u;
8284
hyper_code::HYPERE_OK
8385
},
8486
Err(_) => {
@@ -98,7 +100,8 @@ ffi_fn! {
98100
fn hyper_request_set_version(req: *mut hyper_request, version: c_int) -> hyper_code {
99101
use http::Version;
100102

101-
*unsafe { &mut *req }.0.version_mut() = match version {
103+
let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG);
104+
*req.0.version_mut() = match version {
102105
super::HYPER_HTTP_VERSION_NONE => Version::HTTP_11,
103106
super::HYPER_HTTP_VERSION_1_0 => Version::HTTP_10,
104107
super::HYPER_HTTP_VERSION_1_1 => Version::HTTP_11,
@@ -130,8 +133,9 @@ ffi_fn! {
130133
/// This takes ownership of the `hyper_body *`, you must not use it or
131134
/// free it after setting it on the request.
132135
fn hyper_request_set_body(req: *mut hyper_request, body: *mut hyper_body) -> hyper_code {
133-
let body = unsafe { Box::from_raw(body) };
134-
*unsafe { &mut *req }.0.body_mut() = body.0;
136+
let body = non_null!(Box::from_raw(body) ?= hyper_code::HYPERE_INVALID_ARG);
137+
let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG);
138+
*req.0.body_mut() = body.0;
135139
hyper_code::HYPERE_OK
136140
}
137141
}
@@ -157,7 +161,8 @@ ffi_fn! {
157161
func: callback,
158162
data: UserDataPointer(data),
159163
};
160-
unsafe { &mut *req }.0.extensions_mut().insert(ext);
164+
let req = non_null!(&mut *req ?= hyper_code::HYPERE_INVALID_ARG);
165+
req.0.extensions_mut().insert(ext);
161166
hyper_code::HYPERE_OK
162167
}
163168
}
@@ -176,7 +181,7 @@ impl hyper_request {
176181
ffi_fn! {
177182
/// Free an HTTP response after using it.
178183
fn hyper_response_free(resp: *mut hyper_response) {
179-
drop(unsafe { Box::from_raw(resp) });
184+
drop(non_null!(Box::from_raw(resp) ?= ()));
180185
}
181186
}
182187

@@ -185,7 +190,7 @@ ffi_fn! {
185190
///
186191
/// It will always be within the range of 100-599.
187192
fn hyper_response_status(resp: *const hyper_response) -> u16 {
188-
unsafe { &*resp }.0.status().as_u16()
193+
non_null!(&*resp ?= 0).0.status().as_u16()
189194
}
190195
}
191196

@@ -200,7 +205,7 @@ ffi_fn! {
200205
/// Use `hyper_response_reason_phrase_len()` to get the length of this
201206
/// buffer.
202207
fn hyper_response_reason_phrase(resp: *const hyper_response) -> *const u8 {
203-
unsafe { &*resp }.reason_phrase().as_ptr()
208+
non_null!(&*resp ?= std::ptr::null()).reason_phrase().as_ptr()
204209
} ?= std::ptr::null()
205210
}
206211

@@ -209,7 +214,7 @@ ffi_fn! {
209214
///
210215
/// Use `hyper_response_reason_phrase()` to get the buffer pointer.
211216
fn hyper_response_reason_phrase_len(resp: *const hyper_response) -> size_t {
212-
unsafe { &*resp }.reason_phrase().len()
217+
non_null!(&*resp ?= 0).reason_phrase().len()
213218
}
214219
}
215220

@@ -226,7 +231,8 @@ ffi_fn! {
226231
/// The buffer is not null-terminated, see the `hyper_buf` functions for
227232
/// getting the bytes and length.
228233
fn hyper_response_headers_raw(resp: *const hyper_response) -> *const hyper_buf {
229-
match unsafe { &*resp }.0.extensions().get::<RawHeaders>() {
234+
let resp = non_null!(&*resp ?= std::ptr::null());
235+
match resp.0.extensions().get::<RawHeaders>() {
230236
Some(raw) => &raw.0,
231237
None => std::ptr::null(),
232238
}
@@ -245,7 +251,7 @@ ffi_fn! {
245251
fn hyper_response_version(resp: *const hyper_response) -> c_int {
246252
use http::Version;
247253

248-
match unsafe { &*resp }.0.version() {
254+
match non_null!(&*resp ?= 0).0.version() {
249255
Version::HTTP_10 => super::HYPER_HTTP_VERSION_1_0,
250256
Version::HTTP_11 => super::HYPER_HTTP_VERSION_1_1,
251257
Version::HTTP_2 => super::HYPER_HTTP_VERSION_2,
@@ -269,7 +275,7 @@ ffi_fn! {
269275
///
270276
/// It is safe to free the response even after taking ownership of its body.
271277
fn hyper_response_body(resp: *mut hyper_response) -> *mut hyper_body {
272-
let body = std::mem::take(unsafe { &mut *resp }.0.body_mut());
278+
let body = std::mem::take(non_null!(&mut *resp ?= std::ptr::null_mut()).0.body_mut());
273279
Box::into_raw(Box::new(hyper_body(body)))
274280
} ?= std::ptr::null_mut()
275281
}
@@ -331,7 +337,7 @@ ffi_fn! {
331337
/// The callback should return `HYPER_ITER_CONTINUE` to keep iterating, or
332338
/// `HYPER_ITER_BREAK` to stop.
333339
fn hyper_headers_foreach(headers: *const hyper_headers, func: hyper_headers_foreach_callback, userdata: *mut c_void) {
334-
let headers = unsafe { &*headers };
340+
let headers = non_null!(&*headers ?= ());
335341
// For each header name/value pair, there may be a value in the casemap
336342
// that corresponds to the HeaderValue. So, we iterator all the keys,
337343
// and for each one, try to pair the originally cased name with the value.
@@ -366,7 +372,7 @@ ffi_fn! {
366372
///
367373
/// This overwrites any previous value set for the header.
368374
fn hyper_headers_set(headers: *mut hyper_headers, name: *const u8, name_len: size_t, value: *const u8, value_len: size_t) -> hyper_code {
369-
let headers = unsafe { &mut *headers };
375+
let headers = non_null!(&mut *headers ?= hyper_code::HYPERE_INVALID_ARG);
370376
match unsafe { raw_name_value(name, name_len, value, value_len) } {
371377
Ok((name, value, orig_name)) => {
372378
headers.headers.insert(&name, value);
@@ -384,7 +390,7 @@ ffi_fn! {
384390
/// If there were already existing values for the name, this will append the
385391
/// new value to the internal list.
386392
fn hyper_headers_add(headers: *mut hyper_headers, name: *const u8, name_len: size_t, value: *const u8, value_len: size_t) -> hyper_code {
387-
let headers = unsafe { &mut *headers };
393+
let headers = non_null!(&mut *headers ?= hyper_code::HYPERE_INVALID_ARG);
388394

389395
match unsafe { raw_name_value(name, name_len, value, value_len) } {
390396
Ok((name, value, orig_name)) => {

src/ffi/io.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ ffi_fn! {
4646
/// This is typically only useful if you aren't going to pass ownership
4747
/// of the IO handle to hyper, such as with `hyper_clientconn_handshake()`.
4848
fn hyper_io_free(io: *mut hyper_io) {
49-
drop(unsafe { Box::from_raw(io) });
49+
drop(non_null!(Box::from_raw(io) ?= ()));
5050
}
5151
}
5252

@@ -55,7 +55,7 @@ ffi_fn! {
5555
///
5656
/// This value is passed as an argument to the read and write callbacks.
5757
fn hyper_io_set_userdata(io: *mut hyper_io, data: *mut c_void) {
58-
unsafe { &mut *io }.userdata = data;
58+
non_null!(&mut *io ?= ()).userdata = data;
5959
}
6060
}
6161

@@ -77,7 +77,7 @@ ffi_fn! {
7777
/// If there is an irrecoverable error reading data, then `HYPER_IO_ERROR`
7878
/// should be the return value.
7979
fn hyper_io_set_read(io: *mut hyper_io, func: hyper_io_read_callback) {
80-
unsafe { &mut *io }.read = func;
80+
non_null!(&mut *io ?= ()).read = func;
8181
}
8282
}
8383

@@ -96,7 +96,7 @@ ffi_fn! {
9696
/// If there is an irrecoverable error reading data, then `HYPER_IO_ERROR`
9797
/// should be the return value.
9898
fn hyper_io_set_write(io: *mut hyper_io, func: hyper_io_write_callback) {
99-
unsafe { &mut *io }.write = func;
99+
non_null!(&mut *io ?= ()).write = func;
100100
}
101101
}
102102

0 commit comments

Comments
 (0)