-
Notifications
You must be signed in to change notification settings - Fork 254
Description
Thank you for your outstanding contributions to this project!
Description
The url_encode function in curl-rust provides a safe interface for URL-encoding a byte slice &[u8]. Internally, it calls the libcurl C function curl_easy_escape.
According to the official libcurl documentation, curl_easy_escape has a strict limitation on its input size:
This function does not accept input strings longer than CURL_MAX_INPUT_LENGTH (8 MB).
The current implementation of the src/easy/handler.rs:3258, url_encode wrapper does not validate the length of the input slice before passing it to the C function. It directly casts s.len() to c_int and calls curl_easy_escape.
// In `fn url_encode`:
pub fn url_encode(&mut self, s: &[u8]) -> String {
if s.is_empty() {
return String::new();
}
unsafe {
let p = curl_sys::curl_easy_escape(
self.inner.handle,
s.as_ptr() as *const _,
s.len() as c_int, // <-- VULNERABILITY: No check if s.len() > CURL_MAX_INPUT_LENGTH
);
assert!(!p.is_null());
let ret = str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap();
let ret = String::from(ret);
curl_sys::curl_free(p as *mut _);
ret
}
}On the C side, curl_easy_escape will call curlx_dyn_init for initialization. The code is as follows:
/*
* Init a dynbuf struct.
*/
void curlx_dyn_init(struct dynbuf *s, size_t toobig)
{
DEBUGASSERT(s);
DEBUGASSERT(toobig);
DEBUGASSERT(toobig <= MAX_DYNBUF_SIZE); /* catch crazy mistakes */
s->bufr = NULL;
s->leng = 0;
s->allc = 0;
s->toobig = toobig;
#ifdef DEBUGBUILD
s->init = DYNINIT;
#endif
}During initialization, assert checks are only performed in debug compilation mode, while such checks are not carried out in release mode.
This omission allows a caller from safe Rust to violate the C function's contract, leading to Undefined Behavior (UB) within the C library.
Impact
An attacker can trigger this vulnerability by passing a slice larger than 8 MB to the url_encode function. This violates the precondition of curl_easy_escape and can lead to memory corruption, denial of service (crash), or other unpredictable program behavior. Because this can be triggered from safe Rust, it constitutes a soundness vulnerability in the crate.
Suggested Fix
The url_encode function must validate the input slice's length before calling the unsafe C function. If the length exceeds CURL_MAX_INPUT_LENGTH, the function should panic or return an error to prevent the unsafe call.
A possible implementation:
// Define the constant based on libcurl's documentation
const CURL_MAX_INPUT_LENGTH: usize = 8 * 1024 * 1024;
// In `fn url_encode`:
if s.len() > CURL_MAX_INPUT_LENGTH {
// Or return an Err(..)
panic!("Input length ({}) exceeds the maximum allowed by libcurl ({})", s.len(), CURL_MAX_INPUT_LENGTH);
}If possible, I can initiate a PR later to fix this issue.