Skip to content

Commit 3ce51e4

Browse files
gkorlandCopilot
andauthored
Fix crash on dump command (#440)
* Initial plan * Fix crash on binary data from Redis commands like DUMP Handle non-UTF-8 binary data by using StringBuffer variant instead of panicking on unwrap. Co-authored-by: gkorland <753206+gkorland@users.noreply.github.com> * Add defensive error handling for BigNumber and VerbatimString conversions Apply the same graceful error handling pattern to BigNumber and VerbatimString to prevent potential panics. Co-authored-by: gkorland <753206+gkorland@users.noreply.github.com> * fix fmt --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: gkorland <753206+gkorland@users.noreply.github.com>
1 parent 3a0257d commit 3ce51e4

File tree

3 files changed

+64
-5
lines changed

3 files changed

+64
-5
lines changed

examples/call.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,34 @@ fn call_blocking_from_detach_ctx(ctx: &Context, _: Vec<RedisString>) -> RedisRes
156156
Ok(RedisValue::NoReply)
157157
}
158158

159+
fn call_dump_test(ctx: &Context, _: Vec<RedisString>) -> RedisResult {
160+
// Set a key with a value
161+
ctx.call("SET", &["test_dump_key", "test_value"])?;
162+
163+
// Call DUMP which returns binary data (may not be valid UTF-8)
164+
let dump_result = ctx.call("DUMP", &["test_dump_key"])?;
165+
166+
// Verify we got a result (should be StringBuffer for binary data)
167+
match dump_result {
168+
RedisValue::StringBuffer(data) => {
169+
if data.is_empty() {
170+
return Err(RedisError::Str("DUMP returned empty data"));
171+
}
172+
}
173+
RedisValue::SimpleString(_) => {
174+
// Also acceptable if the binary data happens to be valid UTF-8
175+
}
176+
_ => {
177+
return Err(RedisError::Str("DUMP returned unexpected type"));
178+
}
179+
}
180+
181+
// Clean up
182+
ctx.call("DEL", &["test_dump_key"])?;
183+
184+
Ok("pass".into())
185+
}
186+
159187
//////////////////////////////////////////////////////
160188

161189
redis_module! {
@@ -167,5 +195,6 @@ redis_module! {
167195
["call.test", call_test, "", 0, 0, 0, ""],
168196
["call.blocking", call_blocking, "", 0, 0, 0, ""],
169197
["call.blocking_from_detached_ctx", call_blocking_from_detach_ctx, "", 0, 0, 0, ""],
198+
["call.dump_test", call_dump_test, "", 0, 0, 0, ""],
170199
],
171200
}

src/redisvalue.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,10 @@ impl<'root> From<&CallReply<'root>> for RedisValue {
233233
RedisValue::Array(reply.iter().map(|v| (&v).into()).collect())
234234
}
235235
CallReply::I64(reply) => RedisValue::Integer(reply.to_i64()),
236-
CallReply::String(reply) => RedisValue::SimpleString(reply.to_string().unwrap()),
236+
CallReply::String(reply) => reply
237+
.to_string()
238+
.map(RedisValue::SimpleString)
239+
.unwrap_or_else(|| RedisValue::StringBuffer(reply.as_bytes().to_vec())),
237240
CallReply::Null(_) => RedisValue::Null,
238241
CallReply::Map(reply) => RedisValue::Map(
239242
reply
@@ -260,10 +263,20 @@ impl<'root> From<&CallReply<'root>> for RedisValue {
260263
),
261264
CallReply::Bool(reply) => RedisValue::Bool(reply.to_bool()),
262265
CallReply::Double(reply) => RedisValue::Float(reply.to_double()),
263-
CallReply::BigNumber(reply) => RedisValue::BigNumber(reply.to_string().unwrap()),
264-
CallReply::VerbatimString(reply) => {
265-
RedisValue::VerbatimString(reply.to_parts().unwrap())
266-
}
266+
CallReply::BigNumber(reply) => reply
267+
.to_string()
268+
.map(RedisValue::BigNumber)
269+
.unwrap_or_else(|| {
270+
// BigNumber should always be valid UTF-8, but if not, treat as raw bytes
271+
RedisValue::StaticError("Invalid BigNumber: not valid UTF-8")
272+
}),
273+
CallReply::VerbatimString(reply) => reply
274+
.to_parts()
275+
.map(RedisValue::VerbatimString)
276+
.unwrap_or_else(|| {
277+
// VerbatimString format should always be valid UTF-8, but if not, treat as error
278+
RedisValue::StaticError("Invalid VerbatimString: format not valid UTF-8")
279+
}),
267280
}
268281
}
269282
}

tests/integration.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,23 @@ fn test_call_blocking() -> Result<()> {
633633
Ok(())
634634
}
635635

636+
#[test]
637+
#[cfg(any(
638+
feature = "min-redis-compatibility-version-7-4",
639+
feature = "min-redis-compatibility-version-7-2"
640+
))]
641+
fn test_call_dump() -> Result<()> {
642+
let mut con = TestConnection::new("call");
643+
644+
let res: String = redis::cmd("call.dump_test")
645+
.query(&mut con)
646+
.with_context(|| "failed to run call.dump_test")?;
647+
648+
assert_eq!(&res, "pass");
649+
650+
Ok(())
651+
}
652+
636653
#[test]
637654
fn test_open_key_with_flags() -> Result<()> {
638655
let mut con = TestConnection::new("open_key_with_flags");

0 commit comments

Comments
 (0)