Skip to content
This repository has been archived by the owner on Feb 2, 2023. It is now read-only.

Commit

Permalink
Safer string coercions
Browse files Browse the repository at this point in the history
Previously, we blindly assume Ruby strings are UTF-8 and turn them
into Rust Strings (which *are* assumed to be UTF-8). This is clearly
unsafe so this commit adds some checks to cofirm that and generate
type errors appropiately.
  • Loading branch information
chancancode committed Jun 1, 2018
1 parent bda5141 commit 53b7da3
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 14 deletions.
4 changes: 4 additions & 0 deletions crates/libcruby-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ extern "C" {
pub fn rb_define_alloc_func(klass: VALUE, func: extern "C" fn(klass: VALUE) -> VALUE);
pub fn rb_define_method(class: VALUE, name: c_string, func: c_func, arity: isize);
pub fn rb_define_singleton_method(class: VALUE, name: c_string, func: c_func, arity: isize);
pub fn rb_enc_str_asciionly_p(string: VALUE) -> bool;
pub fn rb_enc_str_coderange(string: VALUE) -> bool;
pub fn rb_enc_get_index(obj: VALUE) -> isize;
pub fn rb_utf8_encindex() -> isize;
pub fn rb_sprintf(specifier: c_string, ...) -> VALUE;
pub fn rb_inspect(value: VALUE) -> VALUE;
pub fn rb_intern(string: c_string) -> ID;
Expand Down
16 changes: 14 additions & 2 deletions examples/console/spec/console_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,19 @@
end
end

it "can handle invalid arguments" do
expect { console.log(123) }.to raise_error(TypeError, "Expected a UTF-8 String, got 123")
describe "invalid arguments" do
it "can handle non-strings" do
expect { console.log(123) }.to raise_error(TypeError, "Expected a String, got 123")
end

it "raises on non UTF-8 strings" do
str = "hello".encode("BIG5")
expect { console.log(str) }.to raise_error(TypeError, "Expected an UTF-8 String, got #{str.inspect}")
end

it "raises on invalid UTF-8 strings" do
str = "\330"
expect { console.log(str) }.to raise_error(TypeError, "Expected a valid UTF-8 String, got #{str.inspect}")
end
end
end
32 changes: 20 additions & 12 deletions src/coercions/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,33 @@ use libc;
use std;
use sys;
use sys::{VALUE};
use super::{FromRuby, CheckResult, CheckedValue, ToRuby, ToRubyResult};
use super::{FromRuby, CheckResult, ToRuby, ToRubyResult};

impl FromRuby for String {
type Checked = CheckedValue<String>;
type Checked = &'static str;

fn from_ruby(value: VALUE) -> CheckResult<CheckedValue<String>> {
if unsafe { sys::RB_TYPE_P(value, sys::T_STRING) } {
Ok(unsafe { CheckedValue::new(value) })
fn from_ruby(value: VALUE) -> CheckResult<&'static str> {
if ! unsafe { sys::RB_TYPE_P(value, sys::T_STRING) } {
type_error!(value, "a String")
} else if unsafe { sys::rb_enc_get_index(value) == sys::rb_utf8_encindex() } {
let size = unsafe { sys::RSTRING_LEN(value) };
let ptr = unsafe { sys::RSTRING_PTR(value) };
let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, size as usize) };

std::str::from_utf8(slice).or_else(|_| type_error!(value, "a valid UTF-8 String"))
} else if unsafe { sys::rb_enc_str_asciionly_p(value) } {
let size = unsafe { sys::RSTRING_LEN(value) };
let ptr = unsafe { sys::RSTRING_PTR(value) };
let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, size as usize) };

Ok(unsafe { std::str::from_utf8_unchecked(slice) })
} else {
type_error!(value, "a UTF-8 String")
type_error!(value, "an UTF-8 String")
}
}

fn from_checked(checked: CheckedValue<String>) -> String {
let value = checked.to_value();
let size = unsafe { sys::RSTRING_LEN(value) };
let ptr = unsafe { sys::RSTRING_PTR(value) };
let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, size as usize) };
unsafe { std::str::from_utf8_unchecked(slice) }.to_string()
fn from_checked(checked: &'static str) -> String {
checked.to_string()
}
}

Expand Down

0 comments on commit 53b7da3

Please sign in to comment.