Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -422,32 +422,31 @@ namespace Sad { namespace LLVM {

llvm::Value *SecurityBuiltinsCodeGen::emitBuiltinSecuritySecureRandom(std::shared_ptr<SIRInstruction> inst)
{
// Use BCryptGenRandom on Windows for cryptographic randomness
// Signature: NTSTATUS BCryptGenRandom(BCRYPT_ALG_HANDLE, PUCHAR, ULONG, ULONG)
// We use flag BCRYPT_USE_SYSTEM_PREFERRED_RNG = 2 with NULL handle
// (AR) عشوائي_آمن(الحد_الأدنى، الحد_الأقصى) — يستدعي
// sad_security_secure_random(i64, i64) -> i64 من وقت التشغيل
// المضمَّن (sad_embedded_runtime.c)، بنفس نمط بقيّة عائلة الأمن
// المستضافة (هاش/شفّر/فك_تشفير). هذا يستبدل نداءً سابقًا مباشرًا
// لِـBCryptGenRandom في LLVM IR كان: (أ) يفشل الربط دومًا — لم
// يُربط bcrypt.lib في أيّ من مسارات الربط الثلاثة، و(ب) يتجاهل
// معاملَي الحد الأدنى/الأقصى تمامًا (يُرجع عددًا كاملًا شبه غير
// محدود بدل مدى مطلوب). الآن: منطق التوليد (وحماية Windows/POSIX
// عبر مصدر عشوائيّة النظام) في مكان واحد قابل للاختبار.
// (EN) Calls sad_security_secure_random(i64,i64)->i64 in the embedded
// runtime, matching the rest of the hosted security family. This
// replaces a previous direct BCryptGenRandom call in LLVM IR that
// (a) never linked (bcrypt.lib was linked nowhere) and (b) ignored
// the min/max arguments entirely.
if (!inst || inst->operands.size() < 2)
return nullptr;
llvm::Value *minVal = cg_.resolveOperand(inst->operands[0]);
llvm::Value *maxVal = cg_.resolveOperand(inst->operands[1]);
if (!minVal || !maxVal)
return nullptr;
auto i64Ty = llvm::Type::getInt64Ty(*cg_.context_);
auto i32Ty = llvm::Type::getInt32Ty(*cg_.context_);
auto i8Ty = llvm::Type::getInt8Ty(*cg_.context_);
auto ptrTy = llvm::PointerType::getUnqual(*cg_.context_);

// Allocate 8 bytes on the stack for the random value
llvm::Value *buf = cg_.builder_->CreateAlloca(i64Ty, nullptr, "rng.buf");

// Call BCryptGenRandom(NULL, buf, 8, BCRYPT_USE_SYSTEM_PREFERRED_RNG=2)
auto *bcrType = llvm::FunctionType::get(i32Ty, {ptrTy, ptrTy, i32Ty, i32Ty}, false);
auto bcrFunc = cg_.module_->getOrInsertFunction("BCryptGenRandom", bcrType);
cg_.builder_->CreateCall(bcrFunc, {
llvm::ConstantPointerNull::get(llvm::cast<llvm::PointerType>(ptrTy)),
buf,
llvm::ConstantInt::get(i32Ty, 8),
llvm::ConstantInt::get(i32Ty, 2) // BCRYPT_USE_SYSTEM_PREFERRED_RNG
});

// Load the random value
llvm::Value *result = cg_.builder_->CreateLoad(i64Ty, buf, "rng.val");
// Make it positive by masking off sign bit
result = cg_.builder_->CreateAnd(result, llvm::ConstantInt::get(i64Ty, 0x7FFFFFFFFFFFFFFF), "rng.pos");
if (inst && inst->result.has_value())
llvm::FunctionType *ft = llvm::FunctionType::get(i64Ty, {i64Ty, i64Ty}, false);
llvm::FunctionCallee fn = cg_.module_->getOrInsertFunction("sad_security_secure_random", ft);
llvm::Value *result = cg_.builder_->CreateCall(fn, {minVal, maxVal}, "secure_random.ret");
if (inst->result.has_value())
cg_.context_info_.namedValues[inst->result->name] = result;
return result;
}
Expand Down
26 changes: 23 additions & 3 deletions language-truth/builtins/assertions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,29 @@ functions:
compiler_strategy: RUNTIME_CALL
since: 0.1.0
status: stable
description_ar: عشوائي آمن
description_en: Secure random
params: []
description_ar: >-
يُرجع عددًا صحيحًا عشوائيًّا ضمن المدى [الحدّ_الأدنى، الحدّ_الأقصى]
(كلاهما شامل)، مصدره CSPRNG حقيقيّ لنظام التشغيل — BCryptGenRandom على
Windows، /dev/urandom على Linux/macOS — لا مولِّد أرقام شبه عشوائيّ عاديّ
(rand()/srand()). مطابق بين المفسّر والمترجم منذ توحيد جولة توسيع مكتبة
التشفير (المرحلة ٠). مناسب لتوليد nonces/رموز جلسة/مفاتيح مؤقّتة؛ ليس
توليدًا لمفاتيح تشفير طويلة الأمد بحدّ ذاته (استعمل اشتقاق مفتاح مخصَّص
لذلك عند توفّره).
description_en: >-
Returns a random integer in [min, max] (inclusive) sourced from a real OS
CSPRNG — BCryptGenRandom on Windows, /dev/urandom on Linux/macOS — not a
plain PRNG (rand()/srand()). Identical behavior on interpreter and
compiler since the crypto-library-expansion RFC's phase 0. Suitable for
nonces/session tokens/short-lived keys; not a long-term key-generation
primitive by itself.
params:
- الحدّ_الأدنى — أصغر قيمة ممكنة للناتج (شاملة).
- الحدّ_الأقصى — أكبر قيمة ممكنة للناتج (شاملة).
examples:
- code: |
استورد تأكيدات
اطبع_سطر(عشوائي_آمن(1، 100) >= 1)
runnable: true
lookup_table: name_only
- cpp_id: BASE64_ENCODE
canonical: ترميز_64
Expand Down
4 changes: 2 additions & 2 deletions shared/builtins/generated/builtin_registry_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# @description عشوائي_آمن — التزام بالمدى [الأدنى،الأقصى] + عدم تكرار حتميّ؛ شعاع تراجع (regression) لثغرتين حقيقيّتين في مسار المترجم أصلحتهما هذه الجولة (توسيع مكتبة التشفير، المرحلة ٠)
# @expected: 5\nصحيح\nصحيح
# @priority P1

# ============================================================
# قبل هذه الجولة، مسار المترجم (sadc) لِـعشوائي_آمن كان معطوبًا بعلّتين:
# ١) يستدعي BCryptGenRandom مباشرة في LLVM IR دون ربط bcrypt.lib في أيّ
# من مسارات الربط الثلاثة — فشل الربط (undefined symbol) دومًا على
# Windows؛ أيّ برنامج .ص مُصرَّف يستدعي عشوائي_آمن كان يفشل البناء.
# ٢) حتى لو رُبط، كان يتجاهل معاملَي الحدّ الأدنى/الأقصى بالكامل ويُرجع
# عددًا كاملًا شبه غير محدود بدل قيمة ضمن المدى المطلوب.
# الإصلاح: عشوائي_آمن يستدعي الآن sad_security_secure_random(i64,i64)->i64
# من وقت التشغيل المضمَّن (نفس نمط هاش/شفّر/فك_تشفير)، الذي يستعمل مصدر
# عشوائيّة نظام التشغيل الحقيقيّ (BCryptGenRandom عبر تحميل ديناميكيّ على
# Windows، /dev/urandom على POSIX) بدل srand(time(NULL))+rand() القابل
# للتنبّؤ القديم.
# ============================================================
استورد تأكيدات

# نطاق نقطة واحدة [5،5] — يجب أن يكون الناتج 5 دائمًا (يتحقّق من احترام
# المعاملين لا تجاهلهما).
اطبع_سطر(عشوائي_آمن(5، 5))

# نطاق واسع [1،100] عبر 50 استدعاء — يتحقّق أنّ كل قيمة ضمن المدى فعلًا.
متغير ك = 0
متغير ضمن_النطاق = صحيح
بينما (ك < 50)
متغير ع = عشوائي_آمن(1، 100)
إذا (ع < 1 أو ع > 100)
ضمن_النطاق = خطأ
نهاية
ك = ك + 1
نهاية
اطبع_سطر(ضمن_النطاق)

# عدم تكرار حتميّ: نطاق واسع جدًّا [1،1000000000] — احتمال تطابق قيمتين
# ضئيل جدًّا (~1 في المليار) إن كان المصدر عشوائيًّا فعلًا؛ لو عاد الباغ
# القديم (بذرة الوقت بالثانية) لكانت القيمتان متطابقتين حتمًا عند تشغيل
# الاستدعاءين ضمن نفس الثانية.
متغير أ = عشوائي_آمن(1، 1000000000)
متغير ب = عشوائي_آمن(1، 1000000000)
اطبع_سطر(أ != ب)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# COVERAGE — 09_المكتبة_القياسية (129 ملفاً)
# COVERAGE — 09_المكتبة_القياسية (130 ملفاً)

> جدول تدقيق التغطية (GR-01): كل ميزة في هذا القسم ↔ اختبار يغطّيها.
> ✅ مغطّى · ⚠️ جزئي · ❌ فجوة. حدِّثه ضمن DoD أي ميزة جديدة.
Expand All @@ -11,7 +11,7 @@
| رياضيات (جذر/لوغاريتم/أس/π) | ✅ | `02_رياضيات/` (6) | — |
| نصوص متقدمة | ✅ | `03_نصوص/` (8) | — |
| شبكة (TCP/HTTP/websocket) | ⚠️ | `05_شبكة/` (90) | — |
| تشفير (هاش/شفّر/فك_تشفير — SHA-256/SHA-256-CTR) | ✅ | `04_تشفير/` (4) | — |
| تشفير (هاش/شفّر/فك_تشفير/عشوائي_آمن) | ✅ | `04_تشفير/` (5) | — |
| أساسيات (ملفات/مدى/تأكد) | ❌ | — | — |
| خرائط متقدمة/JSON/تاريخ/regex | ❌ | — | — |
| مقابس | ❌ | — | — |
Expand All @@ -31,3 +31,11 @@
متباعد فعليًّا بين المحرِّكين (استثناء قابل للالتقاط بالمفسّر مقابل رسالة stderr بالمترجم بلا
استثناء لغويّ) وغير مُغلَق؛ راجع RISK.md. `_negative/` في هذا القسم فارغ حاليًّا في كل المستودع —
لا سابقة عمل حيّة لتوليد اختبار سلبي منها.
- **`عشوائي_آمن` (المرحلة ٠ من RFC توسيع مكتبة التشفير):** فحص السوت كشف عِلَّتين حقيقيّتين
في مسار المترجم كانتا تسبقان هذه الجولة — (أ) نداء مباشر لِـ`BCryptGenRandom` في LLVM IR
دون ربط `bcrypt.lib` في أيّ من مسارات الربط الثلاثة، فيفشل الربط دومًا على Windows؛
(ب) تجاهل تامّ لمعاملَي الحدّ الأدنى/الأقصى. أُصلحتا معًا: الآن يستدعي المترجم
`sad_security_secure_random` من وقت التشغيل المضمَّن (نفس نمط بقيّة عائلة الأمن)، ومصدر
العشوائيّة نفسه CSPRNG حقيقيّ (BCryptGenRandom/`/dev/urandom` حسب المنصّة) بدل
`srand(time(NULL))+rand()` القابل للتنبّؤ القديم. `154_stdlib_security_secure_random.ص`
شعاع تراجع لكلتا العلّتين (التزام المدى + عدم تكرار حتميّ عبر 10 جولات burn-in).
32 changes: 28 additions & 4 deletions tools/compiler/compiler_driver_android_linker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,12 +340,36 @@ long long sad_security_timestamp(void) {
return (long long)time(NULL);
}

/* أندرويد نظام تشغيل مضيف دائمًا (لا وضع حرّ) — /dev/urandom متاح دون أذونات
* خاصّة على كل إصدارات API، فلا حاجة لتحميل مكتبة NDK إضافيّة أو تمييز حرّ/مضيف
* كما في sad_embedded_runtime.c. */
static int sad_crypto_os_random(unsigned char* buf, size_t len) {
FILE* f = fopen("/dev/urandom", "rb");
size_t got;
if (!f) return 0;
got = fread(buf, 1, len, f);
fclose(f);
return got == len;
}

long long sad_security_secure_random(long long min_val, long long max_val) {
static int seeded = 0;
if (!seeded) { srand((unsigned int)time(NULL)); seeded = 1; }
unsigned long long range;
if (min_val >= max_val) return min_val;
long long range = max_val - min_val + 1;
return min_val + (long long)(rand() % (int)range);
range = (unsigned long long)(max_val - min_val + 1);
{
unsigned char bytes[8];
if (sad_crypto_os_random(bytes, sizeof(bytes))) {
unsigned long long r = 0;
int i;
for (i = 0; i < 8; ++i) r = (r << 8) | bytes[i];
return min_val + (long long)(r % range);
}
}
{
static int seeded = 0;
if (!seeded) { srand((unsigned int)time(NULL)); seeded = 1; }
return min_val + (long long)(rand() % (int)range);
}
}

)";
Expand Down
16 changes: 16 additions & 0 deletions tools/compiler/compiler_driver_build_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -581,16 +581,32 @@ namespace sad
bool is_clang = (c_compiler.find("clang") != std::string::npos ||
c_compiler.find("CLANG") != std::string::npos);

// (AR) SAD_FREESTANDING يُميَّز فقط عند --freestanding: يُبقي
// sad_embedded_runtime.c على مسار العشوائيّة القديم (srand/rand
// عبر libc فقط، بلا نداء نظام تشغيل) لأهداف الوضع الحرّ التي قد
// لا يوجد تحتها نظام تشغيل مضيف أصلًا (كنواة sad-os). الأهداف
// المستضافة (الافتراضي) تحصل على CSPRNG حقيقيّ — انظر
// sad_crypto_os_random في sad_embedded_runtime.c.
// (EN) SAD_FREESTANDING is defined only for --freestanding builds, so
// sad_embedded_runtime.c keeps the old libc-only rand() path on
// that target (may have no host OS at all, e.g. sad-os kernel).
// Hosted targets (default) get a real OS-backed CSPRNG.
const std::string freestanding_define = options_.freestanding ? "SAD_FREESTANDING" : "";

if (is_msvc)
{
command = "\"" + c_compiler + "\" /c /O2 /TC /W0 /nologo";
command += options_.link_static ? " /MT" : " /MD";
if (!freestanding_define.empty())
command += " /D" + freestanding_define;
command += " /Fo\"" + obj_output_path + "\"";
command += " \"" + c_source_path + "\"";
}
else
{
command = "\"" + c_compiler + "\" -c -O2 -w";
if (!freestanding_define.empty())
command += " -D" + freestanding_define;
#ifdef _WIN32
if (is_clang)
{
Expand Down
Loading
Loading