Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • ashton/relibc
  • vincent/relibc
  • boomshroom/relibc
  • njskalski/relibc
  • bjorn3/relibc
  • microcolonel/relibc
  • gmacd/relibc
  • feliwir/relibc
  • devnexen/relibc
  • jamesgraves/relibc
  • oddcoder/relibc
  • andar1an/relibc
  • gugz0r/relibc
  • matijaskala/relibc
  • zen3ger/relibc
  • Majoneza/relibc
  • enygmator/relibc
  • JustAnotherDev/relibc
  • doriancodes/relibc
  • adamantinum/relibc
  • wiredtv/relibc
  • stratact/relibc
  • Ramla-I/relibc
  • martin/relibc
  • bitstr0m/relibc
  • redox-os/relibc
  • henritel/relibc
  • smckay/relibc
  • xTibor/relibc
  • devajithvs/relibc
  • andypython/relibc
  • t-nil/relibc
  • DataTriny/relibc
  • ids1024/relibc
  • SteveLauC/relibc
  • dlrobertson/relibc
  • AgostonSzepessy/relibc
  • TheDarkula/relibc
  • willnode/relibc
  • bamontan/relibc
  • redoxeon/relibc
  • ayf/relibc
  • heghe/relibc
  • Ivan/relibc
  • hasheddan/relibc
  • dahc/relibc
  • auwardoctor/relibc
  • kodicraft/relibc
  • arthurpaulino/relibc
  • jasonhansel/relibc
  • bpisch/relibc
  • kel/relibc
  • GrayJack/relibc
  • darley/relibc
  • sahitpj/relibc
  • plimkilde/relibc
  • BjornTheProgrammer/relibc
  • defra/relibc
  • Schyrsivochter/relibc
  • ebalalic/relibc
  • adchacon/relibc
  • aaronjanse/relibc
  • josh_williams/relibc
  • 8tab/relibc
  • nicoan/relibc
  • athei/relibc
  • carrot93/relibc
  • RA_GM1/relibc
  • zhaozhao/relibc
  • JCake/relibc
  • KGrewal1/relibc
  • emturner/relibc
  • LuigiPiucco/relibc
  • bfrascher/relibc
  • starsheriff/relibc
  • kcired/relibc
  • jamespcfrancis/relibc
  • neallred/relibc
  • omar-mohamed-khallaf/relibc
  • jD91mZM2/relibc
  • rw_van/relibc
  • Skallwar/relibc
  • matt-vdv/relibc
  • mati865/relibc
  • SoyaOhnishi/relibc
  • ArniDagur/relibc
  • tlam/relibc
  • glongo/relibc
  • kamirr/relibc
  • abdullah/relibc
  • saeedtabrizi/relibc
  • sajattack/relibc
  • lmiskiew/relibc
  • seanpk/relibc
  • MaikuZ/relibc
  • jamadazi/relibc
  • coolreader18/relibc
  • wt/relibc
  • lebensterben/relibc
  • josh/relibc
  • uuuvn/relibc
  • vadorovsky/relibc
  • raffaeleragni/relibc
  • freewilll/relibc
  • LLeny/relibc
  • alfredoyang/relibc
  • batonius/relibc
  • TornaxO7/relibc
  • 4lDO2/relibc
  • Arcterus/relibc
  • Tommoa/relibc
  • samuela/relibc
  • mindriot101/relibc
  • lygstate/relibc
114 results
Show changes
Showing
with 1099 additions and 193 deletions
sys_includes = ["bits/assert.h"]
sys_includes = ["features.h"]
include_guard = "_RELIBC_ASSERT_H"
trailer = "#include <bits/assert.h>"
language = "C"
style = "Tag"
no_includes = true
......@@ -7,6 +8,3 @@ cpp_compat = true
[enum]
prefix_with_name = true
[fn]
no_return = "__attribute__((noreturn))"
//! assert implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/assert.h.html
use crate::{
c_str::CStr,
header::{stdio, stdlib},
platform::types::*,
};
// TODO: set this for entire crate when possible
#![deny(unsafe_op_in_unsafe_fn)]
use crate::{c_str::CStr, platform::types::*};
#[no_mangle]
pub unsafe extern "C" fn __assert_fail(
......@@ -13,17 +12,11 @@ pub unsafe extern "C" fn __assert_fail(
line: c_int,
cond: *const c_char,
) -> ! {
let func = CStr::from_ptr(func).to_str().unwrap();
let file = CStr::from_ptr(file).to_str().unwrap();
let cond = CStr::from_ptr(cond).to_str().unwrap();
let func = unsafe { CStr::from_ptr(func) }.to_str().unwrap();
let file = unsafe { CStr::from_ptr(file) }.to_str().unwrap();
let cond = unsafe { CStr::from_ptr(cond) }.to_str().unwrap();
eprintln!(
"{}: {}:{}: Assertion `{}` failed.",
func,
file,
line,
cond
);
eprintln!("{}: {}:{}: Assertion `{}` failed.", func, file, line, cond);
core::intrinsics::abort();
}
......@@ -11,19 +11,19 @@ after_includes = """
#define PTHREAD_ONCE_INIT ((pthread_once_t){0})
#define PTHREAD_RWLOCK_INITIALIZER ((pthread_rwlock_t){0})
#define pthread_cleanup_push(routine, arg) do { \
struct { \
void (*routine)(void *); \
void *arg; \
void *prev; \
} __relibc_internal_pthread_ll_entry = { \
.routine = (routine), \
.arg = (arg), \
}; \
#define pthread_cleanup_push(ROUTINE, ARG) do { \\
struct { \\
void (*routine)(void *); \\
void *arg; \\
void *prev; \\
} __relibc_internal_pthread_ll_entry = { \\
.routine = (void (*)(void *))(ROUTINE), \\
.arg = (void *)(ARG), \\
}; \\
__relibc_internal_pthread_cleanup_push(&__relibc_internal_pthread_ll_entry);
#define pthread_cleanup_pop(execute) \
__relibc_internal_pthread_cleanup_pop((execute)); \
#define pthread_cleanup_pop(EXECUTE) \\
__relibc_internal_pthread_cleanup_pop((EXECUTE)); \\
} while(0)
"""
......
......@@ -2,11 +2,6 @@
use crate::platform::types::*;
use crate::header::sched::sched_param;
use crate::sync::AtomicLock;
use core::sync::atomic::{AtomicU32 as AtomicUint, AtomicI32 as AtomicInt};
// XXX: https://github.com/eqrion/cbindgen/issues/685
//
// We need to write the opaque types ourselves, and apparently cbindgen doesn't even support
......@@ -16,7 +11,7 @@ use core::sync::atomic::{AtomicU32 as AtomicUint, AtomicI32 as AtomicInt};
#[repr(C)]
pub union pthread_attr_t {
__relibc_internal_size: [c_uchar; 32],
__relibc_internal_align: c_long,
__relibc_internal_align: size_t,
}
#[repr(C)]
pub union pthread_rwlockattr_t {
......@@ -87,8 +82,10 @@ macro_rules! assert_equal_size(
// TODO: Turn into a macro?
#[cfg(all(target_os = "redox", feature = "check_against_libc_crate"))]
const _: () = unsafe {
use ::__libc_only_for_layout_checks as libc;
let export = $export { __relibc_internal_align: 0 };
let _: libc::$export = core::mem::transmute(export.__relibc_internal_size);
let _: libc::$export = core::mem::transmute(export.__relibc_internal_size);
let a = [0_u8; core::mem::align_of::<$export>()];
let b: [u8; core::mem::align_of::<libc::$export>()] = core::mem::transmute(a);
......
#![allow(non_camel_case_types)]
use crate::platform::types::*;
use crate::platform::types::{c_uchar, c_uint};
use alloc::{string::String, vec::Vec};
use base64ct::{Base64Bcrypt, Encoding};
use bcrypt_pbkdf::bcrypt_pbkdf;
use core::str;
const MIN_COST: u32 = 4;
const MAX_COST: u32 = 31;
const BHASH_WORDS: usize = 8;
const BHASH_OUTPUT_SIZE: usize = BHASH_WORDS * 4;
/// Inspired by https://github.com/Keats/rust-bcrypt/blob/87fc59e917bcb6cf3f3752fc7f2b4c659d415597/src/lib.rs#L135
fn split_with_prefix(hash: &str) -> Option<(&str, &str, c_uint)> {
let valid_prefixes = ["2y", "2b", "2a", "2x"];
// Should be [prefix, cost, hash]
let raw_parts: Vec<_> = hash.split('$').skip(1).collect();
if raw_parts.len() != 3 {
return None;
}
let prefix = raw_parts[0];
let setting = raw_parts[2];
if !valid_prefixes.contains(&prefix) {
return None;
}
raw_parts[1]
.parse::<c_uint>()
.ok()
.map(|cost| (prefix, setting, cost))
}
/// Performs Blowfish key derivation on a given password with a specific setting.
///
/// # Parameters
/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
/// * `setting`: The settings for the Blowfish key derivation. It must be a string slice (`&str`)
/// and should follow the format `$<prefix>$<cost>$<setting>`, where `<prefix>` is a string that
/// indicates the type of the hash (e.g., "$2a$"), `<cost>` is a decimal number representing
/// the cost factor for the Blowfish operation, and `<setting>` is a base64-encoded string
/// representing the salt to be used for the Blowfish function.
///
/// # Returns
/// * `Option<String>`: Returns `Some(String)` if the Blowfish operation was successful, where the
/// returned string is the result of the Blowfish operation formatted according to the Modular
/// Crypt Format (MCF). If the Blowfish operation failed, it returns `None`.
///
/// # Errors
/// * If the cost factor is outside the range `[MIN_COST, MAX_COST]`.
///
/// # Example
/// ```
/// let password = "correctbatteryhorsestapler";
/// let setting = "$2y$12$L6Bc/AlTQHyd9liGgGEZyO";
/// let result = crypt_blowfish(password, setting);
/// assert!(result.is_some());
///```
///
/// # Note
/// The `crypt_blowfish` function uses the Blowfish block cipher for hashing.
/// The output of the Blowfish operation is base64-encoded using the BCrypt variant of base64.
pub fn crypt_blowfish(passw: &str, setting: &str) -> Option<String> {
if let Some((prefix, setting, cost)) = split_with_prefix(setting) {
if !(MIN_COST..=MAX_COST).contains(&cost) {
return None;
}
// Passwords need to be null terminated
let mut vec = Vec::with_capacity(passw.len() + 1);
vec.extend_from_slice(passw.as_bytes());
vec.push(0);
// We only consider the first 72 chars; truncate if necessary.
let passw_t = if vec.len() > 72 { &vec[..72] } else { &vec };
let passw: &[c_uchar] = &passw_t;
let setting = setting.as_bytes();
let mut output = vec![0; BHASH_OUTPUT_SIZE + 1];
bcrypt_pbkdf(passw, setting, cost, &mut output).ok()?;
Some(format!(
"${}${}${}",
prefix,
cost,
Base64Bcrypt::encode_string(&output),
))
} else {
None
}
}
include_guard = "_RELIBC_CRYPT_H"
language = "C"
style = "Type"
no_includes = true
cpp_compat = true
[enum]
prefix_with_name = true
\ No newline at end of file
use crate::platform::types::c_uchar;
use alloc::string::String;
use base64ct::{Base64ShaCrypt, Encoding};
use core::str;
use md5_crypto::{Digest, Md5};
// Block size for MD5
const BLOCK_SIZE: usize = 16;
// PWD part length of the password string
const PW_SIZE_MD5: usize = 22;
// Maximum length of a setting
const SALT_MAX: usize = 8;
// Inverse encoding map for MD5.
const MAP_MD5: [c_uchar; BLOCK_SIZE] = [12, 6, 0, 13, 7, 1, 14, 8, 2, 15, 9, 3, 5, 10, 4, 11];
const KEY_MAX: usize = 30000;
fn encode_md5(source: &[c_uchar]) -> Option<[c_uchar; PW_SIZE_MD5]> {
let mut transposed = [0; BLOCK_SIZE];
for (i, &ti) in MAP_MD5.iter().enumerate() {
transposed[i] = source[ti as usize];
}
let mut buf = [0; PW_SIZE_MD5];
Base64ShaCrypt::encode(&transposed, &mut buf).ok()?;
Some(buf)
}
/// Function taken from PR: https://github.com/RustCrypto/password-hashes/pull/351
/// This won't be needed once the PR is merged
fn inner_md5(passw: &str, setting: &str) -> Option<String> {
let mut digest_b = Md5::default();
digest_b.update(passw);
digest_b.update(setting);
digest_b.update(passw);
let hash_b = digest_b.finalize();
let mut digest_a = Md5::default();
digest_a.update(passw);
digest_a.update("$1$");
digest_a.update(setting);
let mut pw_len = passw.len();
let rounds = pw_len / BLOCK_SIZE;
for _ in 0..rounds {
digest_a.update(hash_b);
}
// leftover passw
digest_a.update(&hash_b[..(pw_len - rounds * BLOCK_SIZE)]);
while pw_len > 0 {
match pw_len & 1 {
0 => digest_a.update(&passw[..1]),
1 => digest_a.update([0u8]),
_ => unreachable!(),
}
pw_len >>= 1;
}
let mut hash_a = digest_a.finalize();
// Repeatedly run the collected hash value through MD5 to burn
// CPU cycles
for i in 0..1000_usize {
// new hasher
let mut hasher = Md5::default();
// Add key or last result
if (i & 1) != 0 {
hasher.update(passw);
} else {
hasher.update(hash_a);
}
// Add setting for numbers not divisible by 3
if i % 3 != 0 {
hasher.update(setting);
}
// Add key for numbers not divisible by 7
if i % 7 != 0 {
hasher.update(passw);
}
// Add key or last result
if (i & 1) != 0 {
hasher.update(hash_a);
} else {
hasher.update(passw);
}
// digest_c.clone_from_slice(&hasher.finalize());
hash_a = hasher.finalize();
}
encode_md5(hash_a.as_slice())
.map(|encstr| format!("$1${}${}", setting, str::from_utf8(&encstr).unwrap()))
}
/// Performs MD5 hashing on a given password with a specific setting.
///
/// # Parameters
/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
/// * `setting`: The settings for the MD5 hashing. It must be a string slice (`&str`)
/// and should start with "$1$". The rest of the string should represent the salt
/// to be used for the MD5 hashing.
///
/// # Returns
/// * `Option<String>`: Returns `Some(String)` if the MD5 operation was successful, where the
/// returned string is the result of the MD5 operation formatted according to the Modular
/// Crypt Format (MCF). If the MD5 operation failed, it returns `None`.
///
/// # Errors
/// * If the `passw` length exceeds `KEY_MAX`.
/// * If the `setting` does not start with "$1$".
///
/// # Example
/// ```
/// let password = "my_password";
/// let setting = "$1$saltstring";
/// let result = crypt_md5(password, setting);
/// assert!(result.is_some());
/// ```
///
/// # Note
/// The `crypt_md5` function uses the MD5 hashing algorithm for hashing.
/// The output of the MD5 operation is base64-encoded using the BCrypt variant of base64.
pub fn crypt_md5(passw: &str, setting: &str) -> Option<String> {
/* reject large keys */
if passw.len() > KEY_MAX {
return None;
}
if &setting[0..3] != "$1$" {
return None;
}
let cursor = 3;
let slen = cursor
+ setting[cursor..cursor + SALT_MAX]
.chars()
.take_while(|c| *c != '$')
.count();
let setting = &setting[cursor..slen];
inner_md5(passw, setting)
}
//! `crypt.h` implementation.
//!
//! Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/crypt.3.html>.
// TODO: set this for entire crate when possible
#![deny(unsafe_op_in_unsafe_fn)]
use ::scrypt::password_hash::{Salt, SaltString};
use alloc::{
ffi::CString,
string::{String, ToString},
};
use core::ptr;
use rand::{rngs::SmallRng, RngCore, SeedableRng};
use crate::{
c_str::CStr,
header::{errno::EINVAL, stdlib::rand},
platform::{self, types::*},
};
mod blowfish;
mod md5;
mod pbkdf2;
mod scrypt;
mod sha;
use self::{
blowfish::crypt_blowfish,
md5::crypt_md5,
pbkdf2::crypt_pbkdf2,
scrypt::crypt_scrypt,
sha::{crypt_sha, ShaType::*},
};
/// See <https://www.man7.org/linux/man-pages/man3/crypt.3.html>.
#[repr(C)]
pub struct crypt_data {
initialized: c_int,
buff: [c_char; 256],
}
impl crypt_data {
pub fn new() -> Self {
crypt_data {
initialized: 1,
buff: [0; 256],
}
}
}
fn gen_salt() -> Option<String> {
let mut rng = SmallRng::seed_from_u64(unsafe { rand() as u64 });
let mut bytes = [0u8; Salt::RECOMMENDED_LENGTH];
rng.fill_bytes(&mut bytes);
Some(SaltString::encode_b64(&bytes).ok()?.as_str().to_string())
}
/// See <https://www.man7.org/linux/man-pages/man3/crypt.3.html>.
#[no_mangle]
pub unsafe extern "C" fn crypt_r(
key: *const c_char,
setting: *const c_char,
data: *mut crypt_data,
) -> *mut c_char {
if unsafe { (*data).initialized } == 0 {
unsafe { *data = crypt_data::new() };
}
let key = unsafe { CStr::from_ptr(key) }
.to_str()
.expect("key must be utf-8");
let setting = unsafe { CStr::from_ptr(setting) }
.to_str()
.expect("setting must be utf-8");
let setting_bytes = setting.as_bytes();
let encoded = if setting_bytes[0] == b'$' && setting_bytes[1] != 0 && setting_bytes[2] != 0 {
if setting_bytes[1] == b'1' && setting_bytes[2] == b'$' {
crypt_md5(key, setting)
} else if setting_bytes[1] == b'2' && setting_bytes[3] == b'$' {
crypt_blowfish(key, setting)
} else if setting_bytes[1] == b'5' && setting_bytes[2] == b'$' {
crypt_sha(key, setting, Sha256)
} else if setting_bytes[1] == b'6' && setting_bytes[2] == b'$' {
crypt_sha(key, setting, Sha512)
} else if setting_bytes[1] == b'7' && setting_bytes[2] == b'$' {
crypt_scrypt(key, setting)
} else if setting_bytes[1] == b'8' && setting_bytes[2] == b'$' {
crypt_pbkdf2(key, setting)
} else {
platform::ERRNO.set(EINVAL);
return ptr::null_mut();
}
} else {
None
};
if let Some(inner) = encoded {
let len = inner.len();
if let Ok(ret) = CString::new(inner) {
let ret_ptr = ret.into_raw();
let dst = unsafe { (*data).buff }.as_mut_ptr();
unsafe {
ptr::copy_nonoverlapping(ret_ptr, dst.cast(), len);
}
ret_ptr.cast()
} else {
ptr::null_mut()
}
} else {
ptr::null_mut()
}
}
use super::gen_salt;
use alloc::string::{String, ToString};
use base64ct::{Base64Bcrypt, Encoding};
use core::{str, u32};
use pbkdf2::pbkdf2_hmac;
use sha2::Sha256;
/// Performs PBKDF2 key derivation on a given password with a specific setting.
///
/// # Parameters
/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
/// * `setting`: The settings for the PBKDF2 key derivation. It must be a string slice (`&str`)
/// and should follow the format `$<iter>$<salt>`. The `<iter>` part should be a hexadecimal
/// number representing the iteration count for the PBKDF2 function. The `<salt>` part should
/// be a base64-encoded string representing the salt to be used for the PBKDF2 function.
///
/// # Returns
/// * `Option<String>`: Returns `Some(String)` if the PBKDF2 operation was successful, where the
/// returned string is the result of the PBKDF2 operation formatted according to the Modular
/// Crypt Format (MCF). If the PBKDF2 operation failed, it returns `None`.
///
/// # Errors
/// * If the `setting` does not contain a '$' character.
/// * If the `setting` contains another '$' character after the first one.
/// * If the `<salt>` part of the `setting` is empty.
/// * If the `<iter>` part of the `setting` cannot be converted into a `u32` integer.
///
/// # Example
/// ```
/// let password = "my_password";
/// let setting = "$8$3e8$salt";
/// let result = crypt_pbkdf2(password, setting);
/// assert!(result.is_some());
/// ```
///
/// # Note
/// The `crypt_pbkdf2` function uses the SHA256 hashing algorithm for the PBKDF2 operation.
/// The output of the PBKDF2 operation is base64-encoded using the BCrypt variant of base64.
pub fn crypt_pbkdf2(passw: &str, setting: &str) -> Option<String> {
if let Some((iter_str, salt)) = &setting[3..].split_once('$') {
if salt.contains('$') {
return None;
}
let actual_salt = if salt.len() > 0 {
salt.to_string()
} else {
gen_salt()?
};
let iter = u32::from_str_radix(iter_str, 16).ok()?;
let mut buffer = [0u8; 32];
pbkdf2_hmac::<Sha256>(passw.as_bytes(), actual_salt.as_bytes(), iter, &mut buffer);
Some(format!(
"$8${}${}${}",
iter_str,
salt,
Base64Bcrypt::encode_string(&buffer)
))
} else {
None
}
}
use super::gen_salt;
use crate::platform::types::*;
use alloc::string::{String, ToString};
use base64ct::{Base64Bcrypt, Encoding};
use core::{str, u32};
use scrypt::{scrypt, Params};
/// Map for encoding and decoding
#[inline(always)]
fn to_digit(c: char, radix: u32) -> Option<u32> {
match c {
'.' => Some(0),
'/' => Some(1),
_ => c.to_digit(radix).map(|d| d + 2),
}
}
/// Decodes a 5 character lengt str value to c_uint
///
/// # Arguments
///
/// * `value` - A string slice that represents a u32 value in base64
///
/// # Returns
///
/// * `Option<c_uint>` - Returns the decoded c_uint value if successful, otherwise None
fn dencode_uint(value: &str) -> Option<c_uint> {
if value.len() != 5 {
return None;
}
let result = value
.chars()
.enumerate()
.try_fold(0 as c_uint, |acc, (i, c)| {
acc.checked_add((to_digit(c, 30)? as c_uint) << (i * 6))
});
Some(result?)
}
/// Reads settings for password encryption
///
/// # Arguments
///
/// * `setting` - A string slice that represents the settings
///
/// # Returns
///
/// * `Option<(c_uchar, c_uint, c_uint, String)>` - Returns a tuple containing the settings if successful, otherwise None
fn read_setting(setting: &str) -> Option<(c_uchar, c_uint, c_uint, String)> {
let nlog2 = to_digit(setting.chars().next()?, 30)? as c_uchar;
let r = dencode_uint(&setting[1..6])?;
let p = dencode_uint(&setting[6..11])?;
let salt = &setting[11..];
let actual_salt = if salt.len() > 0 {
salt.to_string()
} else {
gen_salt()?
};
Some((nlog2, r, p, actual_salt))
}
/// Performs Scrypt key derivation on a given password with a specific setting.
///
/// # Parameters
/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
/// * `setting`: The settings for the Scrypt key derivation. It must be a string slice (`&str`)
/// and should follow the format `$<Nlog2>$<r>$<p>$<salt>`. The `<Nlog2>` part should be a decimal
/// number representing the logarithm base 2 of the CPU/memory cost factor N for Scrypt. The `<r>`
/// part should be a decimal number representing the block size r. The `<p>` part should be a decimal
/// number representing the parallelization factor p. The `<salt>` part should be a base64-encoded
/// string representing the salt to be used for the Scrypt function.
///
/// # Returns
/// * `Option<String>`: Returns `Some(String)` if the Scrypt operation was successful, where the
/// returned string is the result of the Scrypt operation formatted according to the Modular
/// Crypt Format (MCF). If the Scrypt operation failed, it returns `None`.
///
/// # Errors
/// * If the `setting` length is less than 14 characters.
/// * If the `scrypt` function fails to perform the Scrypt operation.
///
/// # Example
/// ```
/// let password = "my_password";
/// let setting = "$7$C6..../....SodiumChloride";
/// let result = crypt_scrypt(password, setting);
/// assert!(result.is_some());
/// ```
///
/// # Note
/// The `crypt_scrypt` function uses the Scrypt key derivation function for hashing.
/// The output of the Scrypt operation is base64-encoded using the BCrypt variant of base64.
pub fn crypt_scrypt(passw: &str, setting: &str) -> Option<String> {
if setting.len() < 14 {
return None;
}
let (nlog2, r, p, salt) = read_setting(&setting[3..])?;
let params = Params::new(nlog2, r, p, 32).ok()?;
let mut output = [0u8; 32];
scrypt(passw.as_bytes(), salt.as_bytes(), &params, &mut output).ok()?;
Some(format!(
"$7${}${}${}",
&setting[3..14],
salt,
Base64Bcrypt::encode_string(&output)
))
}
use alloc::string::{String, ToString};
use sha_crypt::{
sha256_crypt_b64, sha512_crypt_b64, Sha256Params, Sha512Params, ROUNDS_DEFAULT, ROUNDS_MAX,
ROUNDS_MIN,
};
use crate::platform::types::*;
// key limit is not part of the original design, added for DoS protection.
// rounds limit has been lowered (versus the reference/spec), also for DoS
// protection. runtime is O(klen^2 + klen*rounds)
const KEY_MAX: usize = 256;
const SALT_MAX: usize = 16;
const RSTRING: &str = "rounds=";
pub enum ShaType {
Sha256,
Sha512,
}
/// Performs SHA hashing on a given password with a specific setting.
///
/// # Parameters
/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
/// * `setting`: The settings for the SHA hashing. It must be a string slice (`&str`)
/// and should start with "$5$" for SHA256 or "$6$" for SHA512. The rest of the string should represent the salt
/// to be used for the SHA hashing.
/// * `cipher`: The type of SHA algorithm to use. It should be either `ShaType::Sha256` or `ShaType::Sha512`.
///
/// # Returns
/// * `Option<String>`: Returns `Some(String)` if the SHA operation was successful, where the
/// returned string is the result of the SHA operation formatted according to the Modular
/// Crypt Format (MCF). If the SHA operation failed, it returns `None`.
///
/// # Errors
/// * If the `passw` length exceeds `KEY_MAX`.
/// * If the `setting` does not start with "$5$" or "$6$".
/// * If the `setting` does not contain a '$' character.
/// * If the `setting` contains another '$' character after the first one.
/// * If the `setting` contains invalid characters.
/// * If the `setting` contains an invalid number of rounds.
/// * If the `sha256_crypt_b64` or `sha512_crypt_b64` function fails to hash the password.
///
/// # Example
/// ```
/// let password = "my_password";
/// let setting = "$5$rounds=1400$anotherlongsaltstringg";
/// let result = crypt_sha(password, setting, ShaType::Sha256);
/// assert!(result.is_some());
/// ```
///
/// # Note
/// The `crypt_sha` function uses the SHA256 or SHA512 hashing algorithm for hashing.
/// The output of the SHA operation is base64-encoded using the BCrypt variant of base64.
pub fn crypt_sha(passw: &str, setting: &str, cipher: ShaType) -> Option<String> {
let mut cursor = 3;
let rounds;
/* reject large keys */
if passw.len() > KEY_MAX {
return None;
}
// SHA256
// setting: $5$rounds=n$setting$ (rounds=n$ and closing $ are optional)
// SHA512
// setting: $6$rounds=n$setting$ (rounds=n$ and closing $ are optional)
let param = match cipher {
ShaType::Sha256 => "$5$",
ShaType::Sha512 => "$6$",
};
if &setting[0..3] != param {
return None;
}
let has_round;
// 7 is len("rounds=")
if &setting[cursor..cursor + 7] == RSTRING {
cursor += 7;
has_round = true;
if let Some(c_end) = setting[cursor..].chars().position(|r| r == '$') {
if let Ok(u) = setting[cursor..cursor + c_end].parse::<c_ulong>() {
cursor += c_end + 1;
rounds = u.min(ROUNDS_MAX as c_ulong).max(ROUNDS_MIN as c_ulong);
} else {
return None;
}
} else {
return None;
}
} else {
has_round = false;
rounds = ROUNDS_DEFAULT as c_ulong;
}
let mut slen = cursor;
for i in 0..SALT_MAX.min(setting.len() - cursor) {
let idx = cursor + i;
if &setting[idx..idx + 1] == "$" {
break;
}
// reject characters that interfere with /etc/shadow parsing
if &setting[idx..idx + 1] == "\n" || &setting[idx..idx + 1] == ":" {
return None;
}
slen += 1;
}
let setting = &setting[cursor..slen];
if let Ok(enc) = match cipher {
ShaType::Sha256 => {
let params = Sha256Params::new(rounds as usize)
.unwrap_or(Sha256Params::new(ROUNDS_DEFAULT).unwrap());
sha256_crypt_b64(passw.as_bytes(), setting.as_bytes(), &params)
}
ShaType::Sha512 => {
let params = Sha512Params::new(rounds as usize)
.unwrap_or(Sha512Params::new(ROUNDS_DEFAULT).unwrap());
sha512_crypt_b64(passw.as_bytes(), setting.as_bytes(), &params)
}
} {
let (r_slice, rn_slice) = if has_round {
(RSTRING, rounds.to_string() + "$")
} else {
("", String::new())
};
Some(format!(
"{}{}{}{}${}",
param, r_slice, rn_slice, setting, enc
))
} else {
None
}
}
sys_includes = ["bits/ctype.h"]
sys_includes = ["bits/ctype.h", "features.h"]
include_guard = "_RELIBC_CTYPE_H"
language = "C"
style = "Tag"
......
//! ctype implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/ctype.h.html
//! `ctype.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/ctype.h.html>.
// TODO: set this for entire crate when possible
#![deny(unsafe_op_in_unsafe_fn)]
// TODO: *_l functions
use crate::platform::types::*;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isalnum.html>.
#[no_mangle]
pub extern "C" fn isalnum(c: c_int) -> c_int {
c_int::from(isdigit(c) != 0 || isalpha(c) != 0)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isalpha.html>.
#[no_mangle]
pub extern "C" fn isalpha(c: c_int) -> c_int {
c_int::from(islower(c) != 0 || isupper(c) != 0)
}
/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/isascii.html>.
///
/// The `isascii()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 7, and removed in Issue 8.
#[deprecated]
#[no_mangle]
pub extern "C" fn isascii(c: c_int) -> c_int {
c_int::from((c & !0x7f) == 0)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isblank.html>.
#[no_mangle]
pub extern "C" fn isblank(c: c_int) -> c_int {
c_int::from(c == c_int::from(b' ') || c == c_int::from(b'\t'))
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/iscntrl.html>.
#[no_mangle]
pub extern "C" fn iscntrl(c: c_int) -> c_int {
c_int::from((c >= 0x00 && c <= 0x1f) || c == 0x7f)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isdigit.html>.
#[no_mangle]
pub extern "C" fn isdigit(c: c_int) -> c_int {
c_int::from(c >= c_int::from(b'0') && c <= c_int::from(b'9'))
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isgraph.html>.
#[no_mangle]
pub extern "C" fn isgraph(c: c_int) -> c_int {
c_int::from(c >= 0x21 && c <= 0x7e)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/islower.html>.
#[no_mangle]
pub extern "C" fn islower(c: c_int) -> c_int {
c_int::from(c >= c_int::from(b'a') && c <= c_int::from(b'z'))
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isprint.html>.
#[no_mangle]
pub extern "C" fn isprint(c: c_int) -> c_int {
c_int::from(c >= 0x20 && c < 0x7f)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ispunct.html>.
#[no_mangle]
pub extern "C" fn ispunct(c: c_int) -> c_int {
c_int::from(
......@@ -57,6 +78,7 @@ pub extern "C" fn ispunct(c: c_int) -> c_int {
)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isspace.html>.
#[no_mangle]
pub extern "C" fn isspace(c: c_int) -> c_int {
c_int::from(
......@@ -69,23 +91,29 @@ pub extern "C" fn isspace(c: c_int) -> c_int {
)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isupper.html>.
#[no_mangle]
pub extern "C" fn isupper(c: c_int) -> c_int {
c_int::from(c >= c_int::from(b'A') && c <= c_int::from(b'Z'))
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isxdigit.html>.
#[no_mangle]
pub extern "C" fn isxdigit(c: c_int) -> c_int {
c_int::from(isdigit(c) != 0 || (c | 32 >= c_int::from(b'a') && c | 32 <= c_int::from(b'f')))
}
/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/toascii.html>.
///
/// The `toascii()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 7, and removed in Issue 8.
#[deprecated]
#[no_mangle]
/// The comment in musl:
/// "nonsense function that should NEVER be used!"
pub extern "C" fn toascii(c: c_int) -> c_int {
c & 0x7f
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tolower.html>.
#[no_mangle]
pub extern "C" fn tolower(c: c_int) -> c_int {
if isupper(c) != 0 {
......@@ -95,6 +123,7 @@ pub extern "C" fn tolower(c: c_int) -> c_int {
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/toupper.html>.
#[no_mangle]
pub extern "C" fn toupper(c: c_int) -> c_int {
if islower(c) != 0 {
......
sys_includes = ["sys/types.h"]
sys_includes = ["sys/types.h", "features.h"]
include_guard = "_RELIBC_DIRENT_H"
language = "C"
style = "Both"
......
//! dirent implementation following http://pubs.opengroup.org/onlinepubs/009695399/basedefs/dirent.h.html
//! `dirent.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dirent.h.html>.
use alloc::boxed::Box;
#![deny(unsafe_op_in_unsafe_fn)]
use alloc::{boxed::Box, vec::Vec};
use core::{mem, ptr};
use crate::{
c_str::CStr,
c_vec::CVec,
error::{Errno, ResultExt, ResultExtPtrMut},
fs::File,
header::{errno, fcntl, stdlib, string},
io::{Seek, SeekFrom},
header::{fcntl, stdlib, string},
platform::{self, types::*, Pal, Sys},
};
const DIR_BUF_SIZE: usize = mem::size_of::<dirent>() * 3;
use super::errno::{EINVAL, EIO, ENOMEM};
const INITIAL_BUFSIZE: usize = 512;
// No repr(C) needed, C won't see the content
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dirent.h.html>.
// No repr(C) needed, as this is a completely opaque struct. Being accessed as a pointer, in C it's
// just defined as `struct DIR`.
pub struct DIR {
file: File,
buf: [c_char; DIR_BUF_SIZE],
// index and len are specified in bytes
index: usize,
len: usize,
buf: Vec<u8>,
buf_offset: usize,
// The last value of d_off, used by telldir
offset: usize,
opaque_offset: u64,
}
impl DIR {
pub fn new(path: CStr) -> Result<Box<Self>, Errno> {
Ok(Box::new(Self {
file: File::open(
path,
fcntl::O_RDONLY | fcntl::O_DIRECTORY | fcntl::O_CLOEXEC,
)?,
buf: Vec::with_capacity(INITIAL_BUFSIZE),
buf_offset: 0,
opaque_offset: 0,
}))
}
fn next_dirent(&mut self) -> Result<*mut dirent, Errno> {
let mut this_dent = self.buf.get(self.buf_offset..).ok_or(Errno(EIO))?;
if this_dent.is_empty() {
let size = loop {
self.buf.resize(self.buf.capacity(), 0_u8);
// TODO: uninitialized memory?
match Sys::getdents(*self.file, &mut self.buf, self.opaque_offset) {
Ok(size) => break size,
Err(Errno(EINVAL)) => {
self.buf
.try_reserve_exact(self.buf.len())
.map_err(|_| Errno(ENOMEM))?;
continue;
}
Err(Errno(other)) => return Err(Errno(other)),
}
};
self.buf.truncate(size);
self.buf_offset = 0;
if size == 0 {
return Ok(core::ptr::null_mut());
}
this_dent = &self.buf;
}
let (this_reclen, this_next_opaque) =
unsafe { Sys::dent_reclen_offset(this_dent, self.buf_offset).ok_or(Errno(EIO))? };
//println!("CDENT {} {}+{}", self.opaque_offset, self.buf_offset, this_reclen);
let next_off = self
.buf_offset
.checked_add(usize::from(this_reclen))
.ok_or(Errno(EIO))?;
if next_off > self.buf.len() {
return Err(Errno(EIO));
}
if this_dent.len() < usize::from(this_reclen) {
// Don't want memory corruption if a scheme is adversarial.
return Err(Errno(EIO));
}
let dent_ptr = this_dent.as_ptr() as *mut dirent;
self.opaque_offset = this_next_opaque;
self.buf_offset = next_off;
Ok(dent_ptr)
}
fn seek(&mut self, off: u64) {
let Ok(_) = Sys::dir_seek(*self.file, off) else {
return;
};
self.buf.clear();
self.buf_offset = 0;
self.opaque_offset = off;
}
fn rewind(&mut self) {
self.opaque_offset = 0;
let Ok(_) = Sys::dir_seek(*self.file, 0) else {
return;
};
self.buf.clear();
self.buf_offset = 0;
self.opaque_offset = 0;
}
fn close(mut self) -> Result<(), Errno> {
// Reference files aren't closed when dropped
self.file.reference = true;
// TODO: result
Sys::close(*self.file)
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dirent.h.html>.
#[repr(C)]
#[derive(Clone)]
pub struct dirent {
......@@ -36,68 +127,77 @@ pub struct dirent {
pub d_name: [c_char; 256],
}
#[no_mangle]
pub unsafe extern "C" fn opendir(path: *const c_char) -> *mut DIR {
let path = CStr::from_ptr(path);
let file = match File::open(
path,
fcntl::O_RDONLY | fcntl::O_DIRECTORY | fcntl::O_CLOEXEC,
) {
Ok(file) => file,
Err(_) => return ptr::null_mut(),
};
#[cfg(target_os = "redox")]
const _: () = {
use core::mem::{offset_of, size_of};
use syscall::dirent::DirentHeader;
Box::into_raw(Box::new(DIR {
file,
buf: [0; DIR_BUF_SIZE],
index: 0,
len: 0,
offset: 0,
}))
}
if offset_of!(dirent, d_ino) != offset_of!(DirentHeader, inode) {
panic!("struct dirent layout mismatch (inode)");
}
if offset_of!(dirent, d_off) != offset_of!(DirentHeader, next_opaque_id) {
panic!("struct dirent layout mismatch (inode)");
}
if offset_of!(dirent, d_reclen) != offset_of!(DirentHeader, record_len) {
panic!("struct dirent layout mismatch (len)");
}
if offset_of!(dirent, d_type) != offset_of!(DirentHeader, kind) {
panic!("struct dirent layout mismatch (kind)");
}
if offset_of!(dirent, d_name) != size_of::<DirentHeader>() {
panic!("struct dirent layout mismatch (name)");
}
};
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/alphasort.html>.
#[no_mangle]
pub unsafe extern "C" fn closedir(dir: *mut DIR) -> c_int {
let mut dir = Box::from_raw(dir);
let ret = Sys::close(*dir.file);
// Reference files aren't closed when dropped
dir.file.reference = true;
pub unsafe extern "C" fn alphasort(first: *mut *const dirent, second: *mut *const dirent) -> c_int {
unsafe { string::strcoll((**first).d_name.as_ptr(), (**second).d_name.as_ptr()) }
}
ret
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/closedir.html>.
#[no_mangle]
pub extern "C" fn closedir(dir: Box<DIR>) -> c_int {
dir.close().map(|()| 0).or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dirfd.html>.
#[no_mangle]
pub unsafe extern "C" fn dirfd(dir: *mut DIR) -> c_int {
*((*dir).file)
pub extern "C" fn dirfd(dir: &mut DIR) -> c_int {
*dir.file
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fdopendir.html>.
#[no_mangle]
pub unsafe extern "C" fn readdir(dir: *mut DIR) -> *mut dirent {
if (*dir).index >= (*dir).len {
let read = Sys::getdents(
*(*dir).file,
(*dir).buf.as_mut_ptr() as *mut dirent,
(*dir).buf.len(),
);
if read <= 0 {
if read != 0 && read != -errno::ENOENT {
platform::errno = -read;
}
return ptr::null_mut();
}
pub unsafe extern "C" fn opendir(path: *const c_char) -> *mut DIR {
let path = unsafe { CStr::from_ptr(path) };
(*dir).index = 0;
(*dir).len = read as usize;
}
DIR::new(path).or_errno_null_mut()
}
let ptr = (*dir).buf.as_mut_ptr().add((*dir).index) as *mut dirent;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_getdents.html>.
// #[no_mangle]
pub extern "C" fn posix_getdents(
fildes: c_int,
buf: *mut c_void,
nbyte: size_t,
flags: c_int,
) -> ssize_t {
unimplemented!();
}
(*dir).offset = (*ptr).d_off as usize;
(*dir).index += (*ptr).d_reclen as usize;
ptr
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/readdir.html>.
#[no_mangle]
pub extern "C" fn readdir(dir: &mut DIR) -> *mut dirent {
dir.next_dirent().or_errno_null_mut()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/readdir.html>.
///
/// # Deprecation
/// The `readdir_r()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 8.
#[deprecated]
// #[no_mangle]
pub extern "C" fn readdir_r(
_dir: *mut DIR,
......@@ -107,27 +207,13 @@ pub extern "C" fn readdir_r(
unimplemented!(); // plus, deprecated
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/rewinddir.html>.
#[no_mangle]
pub unsafe extern "C" fn telldir(dir: *mut DIR) -> c_long {
(*dir).offset as c_long
}
#[no_mangle]
pub unsafe extern "C" fn seekdir(dir: *mut DIR, off: c_long) {
let _ = (*dir).file.seek(SeekFrom::Start(off as u64));
(*dir).offset = off as usize;
(*dir).index = 0;
(*dir).len = 0;
}
#[no_mangle]
pub unsafe extern "C" fn rewinddir(dir: *mut DIR) {
seekdir(dir, 0)
}
#[no_mangle]
pub unsafe extern "C" fn alphasort(first: *mut *const dirent, second: *mut *const dirent) -> c_int {
string::strcoll((**first).d_name.as_ptr(), (**second).d_name.as_ptr())
pub extern "C" fn rewinddir(dir: &mut DIR) {
dir.rewind();
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/alphasort.html>.
#[no_mangle]
pub unsafe extern "C" fn scandir(
dirp: *const c_char,
......@@ -135,7 +221,7 @@ pub unsafe extern "C" fn scandir(
filter: Option<extern "C" fn(_: *const dirent) -> c_int>,
compare: Option<extern "C" fn(_: *mut *const dirent, _: *mut *const dirent) -> c_int>,
) -> c_int {
let dir = opendir(dirp);
let dir = unsafe { opendir(dirp) };
if dir.is_null() {
return -1;
}
......@@ -145,11 +231,11 @@ pub unsafe extern "C" fn scandir(
Err(err) => return -1,
};
let old_errno = platform::errno;
platform::errno = 0;
let old_errno = platform::ERRNO.get();
platform::ERRNO.set(0);
loop {
let entry: *mut dirent = readdir(dir);
let entry: *mut dirent = readdir(unsafe { &mut *dir });
if entry.is_null() {
break;
}
......@@ -160,39 +246,58 @@ pub unsafe extern "C" fn scandir(
}
}
let copy = platform::alloc(mem::size_of::<dirent>()) as *mut dirent;
let copy = unsafe { platform::alloc(mem::size_of::<dirent>()) } as *mut dirent;
if copy.is_null() {
break;
}
ptr::write(copy, (*entry).clone());
unsafe { ptr::write(copy, (*entry).clone()) };
if let Err(_) = vec.push(copy) {
break;
}
}
closedir(dir);
closedir(unsafe { Box::from_raw(dir) });
let len = vec.len();
if let Err(_) = vec.shrink_to_fit() {
return -1;
}
if platform::errno != 0 {
if platform::ERRNO.get() != 0 {
for ptr in &mut vec {
platform::free(*ptr as *mut c_void);
unsafe { platform::free(*ptr as *mut c_void) };
}
-1
} else {
*namelist = vec.leak();
unsafe {
*namelist = vec.leak();
}
platform::errno = old_errno;
stdlib::qsort(
*namelist as *mut c_void,
len as size_t,
mem::size_of::<*mut dirent>(),
mem::transmute(compare),
);
platform::ERRNO.set(old_errno);
unsafe {
stdlib::qsort(
*namelist as *mut c_void,
len as size_t,
mem::size_of::<*mut dirent>(),
mem::transmute(compare),
)
};
len as c_int
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/seekdir.html>.
#[no_mangle]
pub extern "C" fn seekdir(dir: &mut DIR, off: c_long) {
dir.seek(
off.try_into()
.expect("off must come from telldir, thus never negative"),
);
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/telldir.html>.
#[no_mangle]
pub extern "C" fn telldir(dir: &mut DIR) -> c_long {
dir.opaque_offset as c_long
}
//! dl-tls implementation for Redox
#![deny(unsafe_op_in_unsafe_fn)]
use core::alloc::Layout;
use alloc::alloc::alloc_zeroed;
use crate::{ld_so::tcb::Tcb, platform::types::*};
#[repr(C)]
......@@ -10,38 +16,64 @@ pub struct dl_tls_index {
#[no_mangle]
pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void {
let tcb = unsafe { Tcb::current().unwrap() };
let ti = unsafe { &*ti };
let masters = unsafe { tcb.masters().unwrap() };
trace!(
"__tls_get_addr({:p}: {:#x}, {:#x})",
"__tls_get_addr({:p}: {:#x}, {:#x}, masters_len={}, dtv_len={})",
ti,
(*ti).ti_module,
(*ti).ti_offset
ti.ti_module,
ti.ti_offset,
masters.len(),
tcb.dtv_mut().len()
);
if let Some(tcb) = Tcb::current() {
if let Some(masters) = tcb.masters() {
if let Some(master) = masters.get((*ti).ti_module as usize) {
let addr = tcb.tls_end.sub(master.offset).add((*ti).ti_offset as usize);
trace!(
"__tls_get_addr({:p}: {:#x}, {:#x}) = {:p}",
ti,
(*ti).ti_module,
(*ti).ti_offset,
addr
);
return addr as *mut c_void;
}
}
if tcb.dtv_mut().len() < masters.len() {
// Reallocate DTV.
tcb.setup_dtv(masters.len());
}
panic!(
"__tls_get_addr({:p}: {:#x}, {:#x}) failed",
ti,
(*ti).ti_module,
(*ti).ti_offset
);
let dtv_index = ti.ti_module as usize - 1;
if tcb.dtv_mut()[dtv_index].is_null() {
// Allocate TLS for module.
let master = &masters[dtv_index];
// FIXME(andypython): master.align
let layout = unsafe {
Layout::from_size_align_unchecked(master.offset /* aligned ph.p_memsz */, 16)
};
let module_tls = unsafe { alloc_zeroed(layout) };
unsafe { core::ptr::copy_nonoverlapping(master.ptr, module_tls, master.len) };
// Set the DTV entry.
tcb.dtv_mut()[dtv_index] = module_tls;
}
let mut ptr = tcb.dtv_mut()[dtv_index];
if ptr.is_null() {
panic!(
"__tls_get_addr({ti:p}: {:#x}, {:#x})",
ti.ti_module, ti.ti_offset
);
}
if cfg!(target_arch = "riscv64") {
ptr = unsafe { ptr.add(0x800 + ti.ti_offset as usize) }; // dynamic offsets are 0x800-based on risc-v
} else {
ptr = unsafe { ptr.add(ti.ti_offset as usize) };
}
ptr.cast::<c_void>()
}
// x86 can define a version that does not require stack alignment
#[cfg(target_arch = "x86")]
#[no_mangle]
pub unsafe extern "C" fn ___tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void {
__tls_get_addr(ti)
unsafe { __tls_get_addr(ti) }
}
//! dlfcn implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/dlfcn.h.html
#![deny(unsafe_op_in_unsafe_fn)]
// FIXME(andypython): remove this when #![allow(warnings, unused_variables)] is
// dropped from src/lib.rs.
#![warn(warnings, unused_variables)]
use core::{
ptr, str,
sync::atomic::{AtomicUsize, Ordering},
};
use crate::{c_str::CStr, ld_so::tcb::Tcb, platform::types::*};
use crate::{
c_str::CStr,
ld_so::{
linker::{DlError, ObjectHandle, Resolve, ScopeKind},
tcb::Tcb,
},
platform::types::*,
};
pub const RTLD_LAZY: c_int = 0x0001;
pub const RTLD_NOW: c_int = 0x0002;
pub const RTLD_GLOBAL: c_int = 0x0100;
pub const RTLD_LAZY: c_int = 1 << 0;
pub const RTLD_NOW: c_int = 1 << 1;
pub const RTLD_NOLOAD: c_int = 1 << 2;
pub const RTLD_GLOBAL: c_int = 1 << 8;
pub const RTLD_LOCAL: c_int = 0x0000;
static ERROR_NOT_SUPPORTED: &'static CStr = c_str!("dlfcn not supported");
pub const RTLD_DEFAULT: *mut c_void = 0 as *mut c_void; // XXX: cbindgen doesn't like ptr::null_mut()
static ERROR_NOT_SUPPORTED: &core::ffi::CStr = c"dlfcn not supported";
#[thread_local]
static ERROR: AtomicUsize = AtomicUsize::new(0);
fn set_last_error(error: DlError) {
ERROR.store(error.repr().as_ptr() as usize, Ordering::SeqCst);
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct Dl_info {
dli_fname: *const c_char,
dli_fbase: *mut c_void,
......@@ -26,64 +46,86 @@ pub struct Dl_info {
}
#[no_mangle]
pub unsafe extern "C" fn dladdr(addr: *mut c_void, info: *mut Dl_info) -> c_int {
pub unsafe extern "C" fn dladdr(_addr: *mut c_void, info: *mut Dl_info) -> c_int {
//TODO
(*info).dli_fname = ptr::null();
(*info).dli_fbase = ptr::null_mut();
(*info).dli_sname = ptr::null();
(*info).dli_saddr = ptr::null_mut();
unsafe {
(*info).dli_fname = ptr::null();
(*info).dli_fbase = ptr::null_mut();
(*info).dli_sname = ptr::null();
(*info).dli_saddr = ptr::null_mut();
}
0
}
#[no_mangle]
pub unsafe extern "C" fn dlopen(cfilename: *const c_char, flags: c_int) -> *mut c_void {
//TODO support all sort of flags
let resolve = if flags & RTLD_NOW == RTLD_NOW {
Resolve::Now
} else {
Resolve::Lazy
};
let scope = if flags & RTLD_GLOBAL == RTLD_GLOBAL {
ScopeKind::Global
} else {
ScopeKind::Local
};
let noload = flags & RTLD_NOLOAD == RTLD_NOLOAD;
let filename = if cfilename.is_null() {
None
} else {
Some(str::from_utf8_unchecked(
CStr::from_ptr(cfilename).to_bytes(),
))
unsafe {
Some(str::from_utf8_unchecked(
CStr::from_ptr(cfilename).to_bytes(),
))
}
};
let tcb = match Tcb::current() {
let tcb = match unsafe { Tcb::current() } {
Some(tcb) => tcb,
None => {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
};
if tcb.linker_ptr.is_null() {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
let mut linker = (&*tcb.linker_ptr).lock();
let mut linker = unsafe { (*tcb.linker_ptr).lock() };
let cbs_c = linker.cbs.clone();
let cbs = cbs_c.borrow();
let id = match (cbs.load_library)(&mut linker, filename) {
Err(err) => {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
match (cbs.load_library)(&mut linker, filename, resolve, scope, noload) {
Ok(handle) => handle.as_ptr().cast_mut(),
Err(error) => {
set_last_error(error);
ptr::null_mut()
}
Ok(id) => id,
};
id as *mut c_void
}
}
#[no_mangle]
pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void {
let handle = ObjectHandle::from_ptr(handle);
if symbol.is_null() {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
let symbol_str = str::from_utf8_unchecked(CStr::from_ptr(symbol).to_bytes());
let symbol_str = unsafe { str::from_utf8_unchecked(CStr::from_ptr(symbol).to_bytes()) };
let tcb = match Tcb::current() {
// FIXME(andypython): just call obj.scope.get_sym() directly or search the
// global scope. The rest is unnecessary as Linker::get_sym() does not
// depend on the Linker state.
let tcb = match unsafe { Tcb::current() } {
Some(tcb) => tcb,
None => {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
......@@ -96,10 +138,10 @@ pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *m
return ptr::null_mut();
}
let linker = (&*tcb.linker_ptr).lock();
let linker = unsafe { (*tcb.linker_ptr).lock() };
let cbs_c = linker.cbs.clone();
let cbs = cbs_c.borrow();
match (cbs.get_sym)(&linker, handle as usize, symbol_str) {
match (cbs.get_sym)(&linker, handle, symbol_str) {
Some(sym) => sym,
_ => {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
......@@ -110,7 +152,7 @@ pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *m
#[no_mangle]
pub unsafe extern "C" fn dlclose(handle: *mut c_void) -> c_int {
let tcb = match Tcb::current() {
let tcb = match unsafe { Tcb::current() } {
Some(tcb) => tcb,
None => {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
......@@ -122,10 +164,16 @@ pub unsafe extern "C" fn dlclose(handle: *mut c_void) -> c_int {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return -1;
};
let mut linker = (&*tcb.linker_ptr).lock();
let Some(handle) = ObjectHandle::from_ptr(handle) else {
set_last_error(DlError::InvalidHandle);
return -1;
};
let mut linker = unsafe { (*tcb.linker_ptr).lock() };
let cbs_c = linker.cbs.clone();
let cbs = cbs_c.borrow();
(cbs.unload)(&mut linker, handle as usize);
(cbs.unload)(&mut linker, handle);
0
}
......
//! `elf.h` implementation.
//!
//! Non-POSIX, see <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
use crate::platform::types::*;
pub type Elf32_Half = uint16_t;
......@@ -27,6 +31,7 @@ pub type Elf64_Versym = Elf64_Half;
pub const EI_NIDENT: usize = 16;
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Ehdr {
pub e_ident: [c_uchar; EI_NIDENT],
......@@ -45,6 +50,7 @@ pub struct Elf32_Ehdr {
pub e_shstrndx: Elf32_Half,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Ehdr {
pub e_ident: [c_uchar; EI_NIDENT],
......@@ -196,6 +202,7 @@ pub const EV_NONE: usize = 0;
pub const EV_CURRENT: usize = 1;
pub const EV_NUM: usize = 2;
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Shdr {
pub sh_name: Elf32_Word,
......@@ -210,6 +217,7 @@ pub struct Elf32_Shdr {
pub sh_entsize: Elf32_Word,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Shdr {
pub sh_name: Elf64_Word,
......@@ -289,6 +297,7 @@ pub const SHF_ORDERED: usize = 1 << 30;
pub const SHF_EXCLUDE: usize = 1 << 31;
pub const GRP_COMDAT: usize = 0x1;
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Sym {
pub st_name: Elf32_Word,
......@@ -299,6 +308,7 @@ pub struct Elf32_Sym {
pub st_shndx: Elf32_Section,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Sym {
pub st_name: Elf64_Word,
......@@ -361,18 +371,21 @@ pub const STV_INTERNAL: usize = 1;
pub const STV_HIDDEN: usize = 2;
pub const STV_PROTECTED: usize = 3;
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Rel {
pub r_offset: Elf32_Addr,
pub r_info: Elf32_Word,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Rel {
pub r_offset: Elf64_Addr,
pub r_info: Elf64_Xword,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Rela {
pub r_offset: Elf32_Addr,
......@@ -380,6 +393,7 @@ pub struct Elf32_Rela {
pub r_addend: Elf32_Sword,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Rela {
pub r_offset: Elf64_Addr,
......@@ -387,6 +401,7 @@ pub struct Elf64_Rela {
pub r_addend: Elf64_Sxword,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Phdr {
pub p_type: Elf32_Word,
......@@ -399,6 +414,7 @@ pub struct Elf32_Phdr {
pub p_align: Elf32_Word,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Phdr {
pub p_type: Elf64_Word,
......@@ -487,6 +503,7 @@ pub union Elf32_Dyn_Union {
d_ptr: Elf32_Addr,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Dyn {
pub d_tag: Elf32_Sword,
......@@ -499,6 +516,7 @@ pub union Elf64_Dyn_Union {
d_ptr: Elf64_Addr,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Dyn {
pub d_tag: Elf64_Sxword,
......@@ -740,6 +758,7 @@ pub const AT_L1D_CACHESHAPE: usize = 35;
pub const AT_L2_CACHESHAPE: usize = 36;
pub const AT_L3_CACHESHAPE: usize = 37;
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Nhdr {
pub n_namesz: Elf32_Word,
......@@ -747,6 +766,7 @@ pub struct Elf32_Nhdr {
pub n_type: Elf32_Word,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Nhdr {
pub n_namesz: Elf64_Word,
......@@ -944,7 +964,7 @@ pub const R_X86_64_RELATIVE64: usize = 38;
pub const R_X86_64_NUM: usize = 39;
#[no_mangle]
pub extern "C" fn stupid_cbindgen_needs_a_function_that_holds_all_elf_structs(
pub extern "C" fn _cbindgen_export_elf(
a: Elf32_Ehdr,
b: Elf64_Ehdr,
c: Elf32_Shdr,
......
sys_includes = ["stdint.h"]
include_guard = "_RELIBC_ENDIAN_H"
trailer = "#include <machine/endian.h>"
language = "C"
style = "Tag"
no_includes = true
cpp_compat = true