Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/11513-crypto-sso-string-args.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**crypto: short runtime-built strings no longer segfault crypto calls (#11481).** A string of 5 bytes or fewer built at runtime is stored inline (SSO, `SHORT_STRING_TAG`); its low 48 bits are length + payload, not an address. Crypto masked every string/bytes argument to 48 bits and handed it to `bytes_from_ptr`, which dereferenced the payload as a `StringHeader*` — so `createHash("sha1").update("x" + n)`, `digest(enc)` with a computed encoding, `createHash(alg)` / `createHmac(alg, key)` with a computed algorithm or key, and pbkdf2/scrypt/hkdf password/salt/digest all segfaulted. Two layers are fixed: the stdlib's f64-argument readers now go through a new `bytes_from_value` (`crypto/util.rs`) that decodes SSO bytes directly, and the codegen crypto arms (`expr/calls/crypto_{hash,kdf,keys,misc}.rs`) unbox string/bytes arguments with `unbox_str_handle` instead of the raw `unbox_to_i64` mask. Covered by `sso_arg_tests` in `crypto/hash_handles.rs` and `test-files/test_gap_11481_crypto_sso_string_args.ts`.
6 changes: 3 additions & 3 deletions crates/perry-codegen/src/expr/calls/crypto_hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,13 @@ pub(crate) fn arm_crypto_hash_chain(
}
}
let blk = ctx.block();
let alg_handle = unbox_to_i64(blk, &alg_box);
let alg_handle = unbox_str_handle(blk, &alg_box);
// Allocate the handle. Both helpers return f64 already
// NaN-boxed with POINTER_TAG, suitable as the receiver
// for `js_native_call_method`.
let recv = if create_method == "createHmac" || create_method == "Hmac" {
let key_box = key_box_opt.expect("createHmac needs a key arg");
let key_handle = unbox_to_i64(blk, &key_box);
let key_handle = unbox_str_handle(blk, &key_box);
blk.call(
DOUBLE,
"js_crypto_create_hmac",
Expand Down Expand Up @@ -337,7 +337,7 @@ pub(crate) fn arm_crypto_create_hash(
// #2013/#3146: reject a non-string algorithm before unboxing.
emit_validate_string_arg(ctx, &alg_box, "algorithm");
let blk = ctx.block();
let alg_handle = unbox_to_i64(blk, &alg_box);
let alg_handle = unbox_str_handle(blk, &alg_box);
// Returns an already-NaN-boxed f64 (POINTER_TAG + handle id).
if let Some(options_box) = options_box {
Ok(blk.call(
Expand Down
48 changes: 24 additions & 24 deletions crates/perry-codegen/src/expr/calls/crypto_kdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub(crate) fn arm_crypto_argon2_sync(
let alg_box = lower_expr(ctx, &args[0])?;
let params_box = lower_expr(ctx, &args[1])?;
let blk = ctx.block();
let alg_handle = unbox_to_i64(blk, &alg_box);
let alg_handle = unbox_str_handle(blk, &alg_box);
let buf_handle = blk.call(
I64,
"js_crypto_argon2_sync",
Expand All @@ -40,7 +40,7 @@ pub(crate) fn arm_crypto_argon2(
let params_box = lower_expr(ctx, &args[1])?;
let cb_box = lower_expr(ctx, &args[2])?;
let blk = ctx.block();
let alg_handle = unbox_to_i64(blk, &alg_box);
let alg_handle = unbox_str_handle(blk, &alg_box);
Ok(blk.call(
DOUBLE,
"js_crypto_argon2_async",
Expand All @@ -63,10 +63,10 @@ pub(crate) fn arm_crypto_hkdf_sync_alg(
let info_box = lower_expr(ctx, &args[3])?;
let len_box = lower_expr(ctx, &args[4])?;
let blk = ctx.block();
let alg_handle = unbox_to_i64(blk, &alg_box);
let ikm_handle = unbox_to_i64(blk, &ikm_box);
let salt_handle = unbox_to_i64(blk, &salt_box);
let info_handle = unbox_to_i64(blk, &info_box);
let alg_handle = unbox_str_handle(blk, &alg_box);
let ikm_handle = unbox_str_handle(blk, &ikm_box);
let salt_handle = unbox_str_handle(blk, &salt_box);
let info_handle = unbox_str_handle(blk, &info_box);
let buf_handle = blk.call(
I64,
"js_crypto_hkdf_bytes_alg",
Expand Down Expand Up @@ -97,10 +97,10 @@ pub(crate) fn arm_crypto_hkdf_async_alg(
let len_box = lower_expr(ctx, &args[4])?;
let cb_box = lower_expr(ctx, &args[5])?;
let blk = ctx.block();
let alg_handle = unbox_to_i64(blk, &alg_box);
let ikm_handle = unbox_to_i64(blk, &ikm_box);
let salt_handle = unbox_to_i64(blk, &salt_box);
let info_handle = unbox_to_i64(blk, &info_box);
let alg_handle = unbox_str_handle(blk, &alg_box);
let ikm_handle = unbox_str_handle(blk, &ikm_box);
let salt_handle = unbox_str_handle(blk, &salt_box);
let info_handle = unbox_str_handle(blk, &info_box);
Ok(blk.call(
DOUBLE,
"js_crypto_hkdf_async_alg",
Expand Down Expand Up @@ -137,8 +137,8 @@ pub(crate) fn arm_crypto_scrypt(
};
let cb_box = lower_expr(ctx, cb_expr)?;
let blk = ctx.block();
let pwd_handle = unbox_to_i64(blk, &pwd_box);
let salt_handle = unbox_to_i64(blk, &salt_box);
let pwd_handle = unbox_str_handle(blk, &pwd_box);
let salt_handle = unbox_str_handle(blk, &salt_box);
Ok(blk.call(
DOUBLE,
"js_crypto_scrypt_async",
Expand Down Expand Up @@ -180,10 +180,10 @@ pub(crate) fn arm_crypto_pbkdf2_sync(
emit_validate_string_arg(ctx, db, "digest");
}
let blk = ctx.block();
let pwd_handle = unbox_to_i64(blk, &pwd_box);
let salt_handle = unbox_to_i64(blk, &salt_box);
let pwd_handle = unbox_str_handle(blk, &pwd_box);
let salt_handle = unbox_str_handle(blk, &salt_box);
let digest_handle = match &digest_box {
Some(b) => unbox_to_i64(blk, b),
Some(b) => unbox_str_handle(blk, b),
None => "0".to_string(),
};
let buf_handle = blk.call(
Expand Down Expand Up @@ -216,9 +216,9 @@ pub(crate) fn arm_crypto_pbkdf2_async(
let alg_box = lower_expr(ctx, &args[4])?;
let cb_box = lower_expr(ctx, &args[5])?;
let blk = ctx.block();
let pwd_handle = unbox_to_i64(blk, &pwd_box);
let salt_handle = unbox_to_i64(blk, &salt_box);
let alg_handle = unbox_to_i64(blk, &alg_box);
let pwd_handle = unbox_str_handle(blk, &pwd_box);
let salt_handle = unbox_str_handle(blk, &salt_box);
let alg_handle = unbox_str_handle(blk, &alg_box);
Ok(blk.call(
DOUBLE,
"js_crypto_pbkdf2_async_alg",
Expand Down Expand Up @@ -253,8 +253,8 @@ pub(crate) fn arm_crypto_scrypt_sync(
// #2013/#3146: node validates keylen as an integer in [0, 2^31-1].
emit_validate_integer_arg(ctx, &keylen_box, "keylen", 0.0, i32::MAX as f64);
let blk = ctx.block();
let pwd_handle = unbox_to_i64(blk, &pwd_box);
let salt_handle = unbox_to_i64(blk, &salt_box);
let pwd_handle = unbox_str_handle(blk, &pwd_box);
let salt_handle = unbox_str_handle(blk, &salt_box);
let buf_handle = blk.call(
I64,
"js_crypto_scrypt_bytes",
Expand Down Expand Up @@ -283,10 +283,10 @@ pub(crate) fn arm_crypto_hkdf_sync(
let info_box = lower_expr(ctx, &args[3])?;
let keylen_box = lower_expr(ctx, &args[4])?;
let blk = ctx.block();
let digest_handle = unbox_to_i64(blk, &digest_box);
let ikm_handle = unbox_to_i64(blk, &ikm_box);
let salt_handle = unbox_to_i64(blk, &salt_box);
let info_handle = unbox_to_i64(blk, &info_box);
let digest_handle = unbox_str_handle(blk, &digest_box);
let ikm_handle = unbox_str_handle(blk, &ikm_box);
let salt_handle = unbox_str_handle(blk, &salt_box);
let info_handle = unbox_str_handle(blk, &info_box);
let buf_handle = blk.call(
I64,
"js_crypto_hkdf_sync",
Expand Down
6 changes: 3 additions & 3 deletions crates/perry-codegen/src/expr/calls/crypto_keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub(crate) fn arm_crypto_create_sign_verify_legacy(
};
let alg_box = lower_expr(ctx, &args[0])?;
let blk = ctx.block();
let alg_handle = unbox_to_i64(blk, &alg_box);
let alg_handle = unbox_str_handle(blk, &alg_box);
let fname = if property == "createSign" || property == "Sign" {
"js_crypto_create_sign"
} else {
Expand All @@ -44,7 +44,7 @@ pub(crate) fn arm_crypto_create_ecdh(
}
let curve_box = lower_expr(ctx, &args[0])?;
let blk = ctx.block();
let curve_handle = unbox_to_i64(blk, &curve_box);
let curve_handle = unbox_str_handle(blk, &curve_box);
Ok(blk.call(DOUBLE, "js_crypto_create_ecdh", &[(I64, &curve_handle)]))
}

Expand Down Expand Up @@ -129,7 +129,7 @@ pub(crate) fn arm_crypto_generate_key_pair_async(
let options = lower_expr(ctx, &args[1])?;
let callback = lower_expr(ctx, &args[2])?;
let blk = ctx.block();
let alg_handle = unbox_to_i64(blk, &alg_box);
let alg_handle = unbox_str_handle(blk, &alg_box);
Ok(blk.call(
DOUBLE,
"js_crypto_generate_key_pair_async",
Expand Down
34 changes: 17 additions & 17 deletions crates/perry-codegen/src/expr/calls/crypto_misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ pub(crate) fn arm_crypto_create_hmac(
emit_validate_string_arg(ctx, &alg_box, "hmac");
emit_validate_crypto_key_arg(ctx, &key_box, "key");
let blk = ctx.block();
let alg_handle = unbox_to_i64(blk, &alg_box);
let key_handle = unbox_to_i64(blk, &key_box);
let alg_handle = unbox_str_handle(blk, &alg_box);
let key_handle = unbox_str_handle(blk, &key_box);
Ok(blk.call(
DOUBLE,
"js_crypto_create_hmac",
Expand Down Expand Up @@ -59,9 +59,9 @@ pub(crate) fn arm_crypto_create_cipheriv(
double_literal(f64::from_bits(crate::nanbox::TAG_UNDEFINED))
};
let blk = ctx.block();
let alg_handle = unbox_to_i64(blk, &alg_box);
let key_handle = unbox_to_i64(blk, &key_box);
let iv_handle = unbox_to_i64(blk, &iv_box);
let alg_handle = unbox_str_handle(blk, &alg_box);
let key_handle = unbox_str_handle(blk, &key_box);
let iv_handle = unbox_str_handle(blk, &iv_box);
let fname = if property == "createCipheriv" {
"js_crypto_create_cipheriv"
} else {
Expand Down Expand Up @@ -144,7 +144,7 @@ pub(crate) fn arm_crypto_create_sign_verify(
}
let alg_box = lower_expr(ctx, &args[0])?;
let blk = ctx.block();
let alg_handle = unbox_to_i64(blk, &alg_box);
let alg_handle = unbox_str_handle(blk, &alg_box);
let fname = if property == "createSign" {
"js_crypto_create_sign"
} else {
Expand Down Expand Up @@ -413,8 +413,8 @@ pub(crate) fn arm_crypto_sign(
None
};
let blk = ctx.block();
let alg_handle = unbox_to_i64(blk, &alg_box);
let data_handle = unbox_to_i64(blk, &data_box);
let alg_handle = unbox_str_handle(blk, &alg_box);
let data_handle = unbox_str_handle(blk, &data_box);
if let Some(callback_box) = callback_box {
return Ok(blk.call(
DOUBLE,
Expand Down Expand Up @@ -454,9 +454,9 @@ pub(crate) fn arm_crypto_verify(
None
};
let blk = ctx.block();
let alg_handle = unbox_to_i64(blk, &alg_box);
let data_handle = unbox_to_i64(blk, &data_box);
let sig_handle = unbox_to_i64(blk, &sig_box);
let alg_handle = unbox_str_handle(blk, &alg_box);
let data_handle = unbox_str_handle(blk, &data_box);
let sig_handle = unbox_str_handle(blk, &sig_box);
if let Some(callback_box) = callback_box {
return Ok(blk.call(
DOUBLE,
Expand Down Expand Up @@ -505,7 +505,7 @@ pub(crate) fn arm_crypto_public_private_crypt(
_ => unreachable!(),
};
let key_handle = blk.call(I64, key_converter, &[(DOUBLE, &key_box)]);
let data_handle = unbox_to_i64(blk, &data_box);
let data_handle = unbox_str_handle(blk, &data_box);
let fname = match property {
"publicEncrypt" => "js_crypto_public_encrypt",
"privateDecrypt" => "js_crypto_private_decrypt",
Expand Down Expand Up @@ -533,9 +533,9 @@ pub(crate) fn arm_crypto_create_secret_key(
None
};
let blk = ctx.block();
let key_handle = unbox_to_i64(blk, &key_box);
let key_handle = unbox_str_handle(blk, &key_box);
let enc_handle = if let Some(enc) = enc_box {
unbox_to_i64(blk, &enc)
unbox_str_handle(blk, &enc)
} else {
"0".to_string()
};
Expand All @@ -559,7 +559,7 @@ pub(crate) fn arm_crypto_generate_key_sync(
let alg_box = lower_expr(ctx, &args[0])?;
let options_box = lower_expr(ctx, &args[1])?;
let blk = ctx.block();
let alg_handle = unbox_to_i64(blk, &alg_box);
let alg_handle = unbox_str_handle(blk, &alg_box);
let buf_handle = blk.call(
I64,
"js_crypto_generate_key_sync",
Expand All @@ -581,7 +581,7 @@ pub(crate) fn arm_crypto_generate_key_async(
let options_box = lower_expr(ctx, &args[1])?;
let cb_box = lower_expr(ctx, &args[2])?;
let blk = ctx.block();
let alg_handle = unbox_to_i64(blk, &alg_box);
let alg_handle = unbox_str_handle(blk, &alg_box);
Ok(blk.call(
DOUBLE,
"js_crypto_generate_key_async",
Expand Down Expand Up @@ -609,7 +609,7 @@ pub(crate) fn arm_crypto_generate_key_pair_sync(
None
};
let blk = ctx.block();
let type_handle = unbox_to_i64(blk, &type_box);
let type_handle = unbox_str_handle(blk, &type_box);
let opts_handle = match &opts_box {
Some(b) => unbox_to_i64(blk, b),
None => "0".to_string(),
Expand Down
11 changes: 3 additions & 8 deletions crates/perry-stdlib/src/crypto/cipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -724,10 +724,7 @@ pub unsafe fn dispatch_cipher(handle: i64, method: &str, args: &[f64]) -> f64 {
let str_bytes = string_bytes_from_arg(args[0]);
decode_string_bytes_with_tag(&str_bytes, in_tag)
}
None => {
let ptr = (args[0].to_bits() & 0x0000_FFFF_FFFF_FFFF) as i64;
bytes_from_ptr(ptr)
}
None => bytes_from_value(args[0]),
};
let previous_len = state.buffer.len();
state.buffer.extend_from_slice(&bytes);
Expand Down Expand Up @@ -1160,8 +1157,7 @@ pub unsafe fn dispatch_cipher(handle: i64, method: &str, args: &[f64]) -> f64 {
if state.encrypt || !state.kind.is_gcm() {
return nanbox_undefined();
}
let ptr = (args[0].to_bits() & 0x0000_FFFF_FFFF_FFFF) as i64;
let tag = bytes_from_ptr(ptr);
let tag = bytes_from_value(args[0]);
state.auth_tag = Some(tag);
nanbox_pointer_f64(handle as usize)
}
Expand All @@ -1172,8 +1168,7 @@ pub unsafe fn dispatch_cipher(handle: i64, method: &str, args: &[f64]) -> f64 {
if args.is_empty() {
state.aad.clear();
} else {
let ptr = (args[0].to_bits() & 0x0000_FFFF_FFFF_FFFF) as i64;
state.aad = bytes_from_ptr(ptr);
state.aad = bytes_from_value(args[0]);
}
nanbox_pointer_f64(handle as usize)
}
Expand Down
23 changes: 10 additions & 13 deletions crates/perry-stdlib/src/crypto/ecdh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub unsafe extern "C" fn js_crypto_create_diffie_hellman(
let second_string = if second_val.is_finite() {
String::new()
} else {
String::from_utf8(bytes_from_ptr(arg_ptr(second_val))).unwrap_or_default()
String::from_utf8(bytes_from_value(second_val)).unwrap_or_default()
};
let (prime_encoding, generator_value, generator_encoding) = if matches!(
second_string.as_str(),
Expand Down Expand Up @@ -110,20 +110,19 @@ pub unsafe extern "C" fn js_crypto_ecdh_convert_key(
output_encoding_val: f64,
format_val: f64,
) -> f64 {
let curve_ptr = arg_ptr(curve_val);
let curve = String::from_utf8(bytes_from_ptr(curve_ptr))
let curve = String::from_utf8(bytes_from_value(curve_val))
.unwrap_or_default()
.to_ascii_lowercase();
if !matches!(curve.as_str(), "prime256v1" | "secp256r1" | "p-256") {
return f64::from_bits(0x7FFC_0000_0000_0001);
}

let input_encoding =
String::from_utf8(bytes_from_ptr(arg_ptr(input_encoding_val))).unwrap_or_default();
String::from_utf8(bytes_from_value(input_encoding_val)).unwrap_or_default();
let output_encoding =
String::from_utf8(bytes_from_ptr(arg_ptr(output_encoding_val))).unwrap_or_default();
let format = String::from_utf8(bytes_from_ptr(arg_ptr(format_val))).unwrap_or_default();
let key_bytes = decode_ecdh_input(arg_ptr(key_val), &input_encoding);
String::from_utf8(bytes_from_value(output_encoding_val)).unwrap_or_default();
let format = String::from_utf8(bytes_from_value(format_val)).unwrap_or_default();
let key_bytes = decode_ecdh_input(key_val, &input_encoding);
let public = match P256PublicKey::from_sec1_bytes(&key_bytes) {
Ok(public) => public,
Err(_) => return f64::from_bits(0x7FFC_0000_0000_0001),
Expand Down Expand Up @@ -159,8 +158,7 @@ pub unsafe fn dispatch_sign(handle: i64, method: &str, args: &[f64]) -> f64 {
}
match method {
"update" if !args.is_empty() => {
let ptr = (args[0].to_bits() & 0x0000_FFFF_FFFF_FFFF) as i64;
let bytes = bytes_from_ptr(ptr);
let bytes = bytes_from_value(args[0]);
h.data.lock().unwrap().extend_from_slice(&bytes);
f64::from_bits(0x7FFD_0000_0000_0000u64 | ((handle as u64) & 0x0000_FFFF_FFFF_FFFF))
}
Expand Down Expand Up @@ -281,7 +279,7 @@ pub unsafe fn dispatch_ecdh(handle: i64, method: &str, args: &[f64]) -> f64 {
};
let input_encoding = arg_string(args, 1);
let output_encoding = arg_string(args, 2);
let public_bytes = decode_ecdh_input(arg_ptr(args[0]), &input_encoding);
let public_bytes = decode_ecdh_input(args[0], &input_encoding);
let public = match P256PublicKey::from_sec1_bytes(&public_bytes) {
Ok(public) => public,
Err(_) => return f64::from_bits(0x7FFC_0000_0000_0001),
Expand Down Expand Up @@ -473,8 +471,7 @@ pub unsafe fn dispatch_verify(handle: i64, method: &str, args: &[f64]) -> f64 {
}
match method {
"update" if !args.is_empty() => {
let ptr = (args[0].to_bits() & 0x0000_FFFF_FFFF_FFFF) as i64;
let bytes = bytes_from_ptr(ptr);
let bytes = bytes_from_value(args[0]);
h.data.lock().unwrap().extend_from_slice(&bytes);
f64::from_bits(0x7FFD_0000_0000_0000u64 | ((handle as u64) & 0x0000_FFFF_FFFF_FFFF))
}
Expand All @@ -491,7 +488,7 @@ pub unsafe fn dispatch_verify(handle: i64, method: &str, args: &[f64]) -> f64 {
encoding_tag_from_arg(args.get(2).copied()).unwrap_or(EncodingTag(0));
decode_string_bytes_with_tag(signature.as_bytes(), encoding)
} else {
bytes_from_ptr(arg_ptr(args[1]))
bytes_from_value(args[1])
};
let pem = match crypto_key_input_to_public_pem(key_bits) {
Some(pem) => pem,
Expand Down
Loading