Integer Overflow in Actix
How Integer Overflow Manifests in Actix
Integer overflow vulnerabilities in Actix applications typically emerge from Rust's default integer arithmetic behavior and Actix's web framework patterns. In Rust, integer operations wrap on overflow in release builds by default, which can lead to unexpected behavior when handling user-controlled values.
A common attack pattern involves manipulating query parameters that get parsed into integers. Consider an Actix endpoint that calculates pagination offsets:
async fn get_items(info: web::Path<(i32, i32)>) -> impl Responder {
let (page, size) = info.into_inner();
let offset = page * size; // Overflow risk here
let items = db.query_items(offset, size).await;
HttpResponse::Ok().json(items)
}
An attacker can trigger overflow by providing extreme values like page=1000000000&size=1000000000, causing the multiplication to wrap around and produce a negative or small offset. This could bypass pagination limits or access unintended database rows.
Another Actix-specific pattern involves parsing JSON request bodies with numeric fields:
#[derive(Deserialize)]
struct CreateOrder {
quantity: i32,
price: i32,
}
async fn create_order(json: web::Json<CreateOrder>) -> impl Responder {
let total = json.quantity * json.price; // Overflow possible
if total < 0 { // Check fails due to wrap
return HttpResponse::BadRequest().finish();
}
// Process order with overflowed total
}
The overflow occurs silently in release builds, making it particularly dangerous. Actix's strong typing doesn't prevent this since the overflow happens at runtime during arithmetic operations, not at the type level.
Actix-Specific Detection
Detecting integer overflow in Actix applications requires both static analysis and runtime scanning. For static analysis, tools like clippy can catch some overflow patterns:
#[clippy::checked]
fn calculate_total(quantity: i32, price: i32) -> i32 {
quantity * price // Clippy warns about potential overflow
}
However, clippy only catches obvious cases and won't detect overflow in release builds where wrapping is the default behavior.
For runtime detection, middleBrick's API security scanner specifically tests Actix endpoints for integer overflow vulnerabilities. The scanner sends boundary values to numeric parameters and analyzes responses for signs of overflow:
$ middlebrick scan https://api.example.com/actix-endpoint
Scan Results:
✓ Authentication: A
✓ BOLA: A
✗ Integer Overflow: C
Finding: Potential integer overflow in /items endpoint
Severity: High
Remediation: Validate input ranges and use checked arithmetic
Details: Test with page=1000000000&size=1000000000 produced unexpected results
The scanner's black-box approach is particularly effective for Actix applications since it tests the actual runtime behavior without requiring source code access. It sends crafted payloads to numeric parameters and checks for anomalies like negative values from positive inputs, or values that wrap around expected ranges.
For comprehensive coverage, combine middleBrick scanning with Actix's built-in validation features:
async fn get_items(
web::Query<PaginationParams> params
) -> impl Responder {
// Parameters are validated before reaching handler
}
#[derive(Deserialize)]
struct PaginationParams {
#[serde(deserialize_with = "validate_positive_i32")]
page: i32,
#[serde(deserialize_with = "validate_positive_i32")]
size: i32,
}
Actix-Specific Remediation
Remediating integer overflow in Actix requires a defense-in-depth approach using Rust's safe arithmetic operations and Actix's validation capabilities. The most robust solution is using checked_* methods that return Option types:
use actix_web::{error, HttpResponse, web};
async fn get_items(
web::Query<PaginationParams> params: web::Query<PaginationParams>
) -> Result<HttpResponse, error::Error> {
let offset = params.page.checked_mul(params.size)
.ok_or_else(|| error::ErrorBadRequest("Integer overflow detected"))?;
let items = db.query_items(offset, params.size).await?;
Ok(HttpResponse::Ok().json(items))
}
#[derive(Deserialize)]
struct PaginationParams {
page: i32,
size: i32,
}
fn validate_positive_i32<'de, D>(deserializer: D) -> Result<i32, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = i32::deserialize(deserializer)?;
if value <= 0 || value > 10000 { // Arbitrary safe limit
return Err(serde::de::Error::custom("Value out of safe range"));
}
Ok(value)
}
This approach ensures that any overflow attempt returns an error before processing continues. The validation limits input ranges to prevent overflow at the multiplication step.
For scenarios requiring larger numbers, use Rust's larger integer types or arbitrary precision arithmetic:
use actix_web::{error, HttpResponse, web};
use num::BigInt;
async fn calculate_total(
web::Json<Order> order: web::Json<Order>
) -> Result<HttpResponse, error::Error> {
let quantity = BigInt::from(order.quantity);
let price = BigInt::from(order.price);
let total = quantity * price;
if total > BigInt::from(i64::MAX) {
return Err(error::ErrorBadRequest("Total exceeds safe limits"));
}
Ok(HttpResponse::Ok().json({
"total": total.to_string()
}))
}
#[derive(Deserialize)]
struct Order {
quantity: i64,
price: i64,
}
The num crate provides arbitrary precision arithmetic that won't overflow, though you still need to validate that results fit within your application's constraints.
For Actix applications, integrate overflow protection into middleware for consistent coverage:
use actix_web::{dev::ServiceRequest, dev::ServiceResponse, middleware::Middleware};
struct OverflowProtection;
impl Middleware<S> for OverflowProtection {
async fn call(&self, req: ServiceRequest, srv: &mut S) -> actix_web::Result {
// Check query parameters for numeric overflow patterns
if let Some(query) = req.uri().query() {
for (key, value) in url::form_urlencoded::parse(query.as_bytes()) {
if let Ok(num) = value.parse::() {
if num > 1_000_000 || num < -1_000_000 {
return Err(actix_web::error::ErrorBadRequest("Parameter out of safe range"));
}
}
}
}
srv.call(req).await
}
}