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
This commit is contained in:
Sean McArthur
2021-08-18 14:15:14 -07:00
committed by GitHub
parent c35153998e
commit 3b26572876
8 changed files with 85 additions and 93 deletions

View File

@@ -1,3 +1,4 @@
use std::ptr;
use std::sync::Arc;
use libc::c_int;
@@ -37,15 +38,8 @@ ffi_fn! {
/// The returned `hyper_task *` must be polled with an executor until the
/// handshake completes, at which point the value can be taken.
fn hyper_clientconn_handshake(io: *mut hyper_io, options: *mut hyper_clientconn_options) -> *mut hyper_task {
if io.is_null() {
return std::ptr::null_mut();
}
if options.is_null() {
return std::ptr::null_mut();
}
let options = unsafe { Box::from_raw(options) };
let io = unsafe { Box::from_raw(io) };
let options = non_null! { Box::from_raw(options) ?= ptr::null_mut() };
let io = non_null! { Box::from_raw(io) ?= ptr::null_mut() };
Box::into_raw(hyper_task::boxed(async move {
options.builder.handshake::<_, crate::Body>(io)
@@ -66,19 +60,12 @@ ffi_fn! {
/// Returns a task that needs to be polled until it is ready. When ready, the
/// task yields a `hyper_response *`.
fn hyper_clientconn_send(conn: *mut hyper_clientconn, req: *mut hyper_request) -> *mut hyper_task {
if conn.is_null() {
return std::ptr::null_mut();
}
if req.is_null() {
return std::ptr::null_mut();
}
let mut req = unsafe { Box::from_raw(req) };
let mut req = non_null! { Box::from_raw(req) ?= ptr::null_mut() };
// Update request with original-case map of headers
req.finalize_request();
let fut = unsafe { &mut *conn }.tx.send_request(req.0);
let fut = non_null! { &mut *conn ?= ptr::null_mut() }.tx.send_request(req.0);
let fut = async move {
fut.await.map(hyper_response::wrap)
@@ -91,7 +78,7 @@ ffi_fn! {
ffi_fn! {
/// Free a `hyper_clientconn *`.
fn hyper_clientconn_free(conn: *mut hyper_clientconn) {
drop(unsafe { Box::from_raw(conn) });
drop(non_null! { Box::from_raw(conn) ?= () });
}
}
@@ -119,7 +106,7 @@ ffi_fn! {
ffi_fn! {
/// Free a `hyper_clientconn_options *`.
fn hyper_clientconn_options_free(opts: *mut hyper_clientconn_options) {
drop(unsafe { Box::from_raw(opts) });
drop(non_null! { Box::from_raw(opts) ?= () });
}
}
@@ -128,9 +115,9 @@ ffi_fn! {
///
/// This does not consume the `options` or the `exec`.
fn hyper_clientconn_options_exec(opts: *mut hyper_clientconn_options, exec: *const hyper_executor) {
let opts = unsafe { &mut *opts };
let opts = non_null! { &mut *opts ?= () };
let exec = unsafe { Arc::from_raw(exec) };
let exec = non_null! { Arc::from_raw(exec) ?= () };
let weak_exec = hyper_executor::downgrade(&exec);
std::mem::forget(exec);
@@ -146,7 +133,7 @@ ffi_fn! {
fn hyper_clientconn_options_http2(opts: *mut hyper_clientconn_options, enabled: c_int) -> hyper_code {
#[cfg(feature = "http2")]
{
let opts = unsafe { &mut *opts };
let opts = non_null! { &mut *opts ?= hyper_code::HYPERE_INVALID_ARG };
opts.builder.http2_only(enabled != 0);
hyper_code::HYPERE_OK
}
@@ -168,7 +155,7 @@ ffi_fn! {
///
/// If enabled, see `hyper_response_headers_raw()` for usage.
fn hyper_clientconn_options_headers_raw(opts: *mut hyper_clientconn_options, enabled: c_int) -> hyper_code {
let opts = unsafe { &mut *opts };
let opts = non_null! { &mut *opts ?= hyper_code::HYPERE_INVALID_ARG };
opts.builder.http1_headers_raw(enabled != 0);
hyper_code::HYPERE_OK
}