2026-05-14 03:22:30 -05:00
//! C harness emitter (stub).
//!
//! No harness source is generated yet — `emit` returns
//! [`UnsupportedReason::LangUnsupported`]. The module exists so that
//! [`crate::dynamic::lang::entry_kinds_supported`] can advertise the entry
//! kinds Track B will deliver (Phase 16: `main(argc, argv)`,
//! `LLVMFuzzerTestOneInput`, free functions with `(const char*, size_t)` or
//! `(int, char**)` shapes) and so the verifier can surface
//! `Inconclusive(EntryKindUnsupported { … })` instead of dropping C findings.
use crate ::dynamic ::lang ::{ HarnessSource , LangEmitter } ;
use crate ::dynamic ::spec ::{ EntryKind , HarnessSpec } ;
use crate ::evidence ::UnsupportedReason ;
/// Zero-sized [`LangEmitter`] handle for C.
pub struct CEmitter ;
/// Entry kinds the C emitter intends to support once Phase 16 lands.
const SUPPORTED : & [ EntryKind ] = & [ EntryKind ::Function ] ;
2026-05-14 05:35:28 -05:00
/// Source of the `__nyx_probe` shim for the (future) C harness (Phase 06 —
/// Track C.1). Variadic over `const char *` args; hand-rolled JSON keeps
/// the only dep on libc / stdio.
pub fn probe_shim ( ) -> & 'static str {
r #"
2026-05-14 13:10:22 -05:00
/* ── __nyx_probe shim (Phase 06 — Track C.1, Phase 08 — Track C.4 + C.5) ── */
#include < signal . h >
2026-05-14 05:35:28 -05:00
#include < stdarg . h >
#include < stdio . h >
#include < stdlib . h >
#include < string . h >
#include < time . h >
2026-05-14 13:10:22 -05:00
#include < unistd . h >
#ifndef __NYX_PAYLOAD_LIMIT
#define __NYX_PAYLOAD_LIMIT ( 16 * 1024 )
#endif
#define __NYX_REDACTED " <redacted-by-nyx-policy> "
extern char * * environ ;
static const char * __nyx_deny [ ] = {
" TOKEN " , " SECRET " , " PASSWORD " , " PASSWD " , " API_KEY " , " APIKEY " , " PRIVATE_KEY " ,
" CREDENTIAL " , " SESSION " , " COOKIE " , " AUTH " , " BEARER " , " AWS_ACCESS " , " AWS_SESSION " ,
" GH_TOKEN " , " GITHUB_TOKEN " , " NPM_TOKEN " , " PYPI_TOKEN " , " DOCKER_PASS " ,
NULL ,
} ;
static int __nyx_is_denied_upper ( const char * k_upper ) {
for ( int i = 0 ; __nyx_deny [ i ] ; + + i ) {
if ( strstr ( k_upper , __nyx_deny [ i ] ) ) return 1 ;
}
return 0 ;
}
static void __nyx_write_witness ( FILE * f , const char * sink_callee , int nargs , const char * * args ) {
fputs ( " { \" env_snapshot \" :{ " , f ) ;
int first = 1 ;
for ( char * * e = environ ; * e ; + + e ) {
const char * eq = strchr ( * e , '=' ) ;
if ( ! eq ) continue ;
size_t klen = ( size_t ) ( eq - * e ) ;
char * kup = ( char * ) malloc ( klen + 1 ) ;
if ( ! kup ) continue ;
for ( size_t i = 0 ; i < klen ; + + i ) {
char c = ( * e ) [ i ] ;
if ( c > = 'a' & & c < = 'z' ) c - = 32 ;
kup [ i ] = c ;
}
kup [ klen ] = '\0' ;
int denied = __nyx_is_denied_upper ( kup ) ;
if ( ! first ) fputc ( ',' , f ) ;
first = 0 ;
fputc ( '"' , f ) ;
fwrite ( * e , 1 , klen , f ) ;
fputs ( " \" : \" " , f ) ;
if ( denied ) {
fputs ( __NYX_REDACTED , f ) ;
} else {
const char * v = eq + 1 ;
for ( ; * v ; + + v ) {
switch ( * v ) {
case '"' : fputs ( " \\ \" " , f ) ; break ;
case '\\' : fputs ( " \\ \\ " , f ) ; break ;
case '\n' : fputs ( " \\ n " , f ) ; break ;
case '\r' : fputs ( " \\ r " , f ) ; break ;
case '\t' : fputs ( " \\ t " , f ) ; break ;
default : fputc ( * v , f ) ;
}
}
}
fputc ( '"' , f ) ;
free ( kup ) ;
}
fputs ( " }, \" cwd \" : \" " , f ) ;
char cwdbuf [ 4096 ] ;
if ( getcwd ( cwdbuf , sizeof ( cwdbuf ) ) ) {
fputs ( cwdbuf , f ) ;
}
fputs ( " \" , \" payload_bytes \" :[ " , f ) ;
const char * payload = getenv ( " NYX_PAYLOAD " ) ;
if ( payload ) {
size_t plen = strlen ( payload ) ;
if ( plen > __NYX_PAYLOAD_LIMIT ) plen = __NYX_PAYLOAD_LIMIT ;
for ( size_t i = 0 ; i < plen ; + + i ) {
if ( i > 0 ) fputc ( ',' , f ) ;
fprintf ( f , " %d " , ( unsigned char ) payload [ i ] ) ;
}
}
fputs ( " ], \" callee \" : \" " , f ) ;
fputs ( sink_callee , f ) ;
fputs ( " \" , \" args_repr \" :[ " , f ) ;
for ( int i = 0 ; i < nargs ; + + i ) {
if ( i > 0 ) fputc ( ',' , f ) ;
fputc ( '"' , f ) ;
if ( args & & args [ i ] ) fputs ( args [ i ] , f ) ;
fputc ( '"' , f ) ;
}
fputs ( " ]} " , f ) ;
}
2026-05-14 05:35:28 -05:00
static void __nyx_probe ( const char * sink_callee , int nargs , .. . ) {
const char * p = getenv ( " NYX_PROBE_PATH " ) ;
if ( ! p | | * p = = '\0' ) return ;
FILE * f = fopen ( p , " a " ) ;
if ( ! f ) return ;
struct timespec ts ;
clock_gettime ( CLOCK_REALTIME , & ts ) ;
unsigned long long ns = ( unsigned long long ) ts . tv_sec * 1000000000 ULL +
( unsigned long long ) ts . tv_nsec ;
const char * pid = getenv ( " NYX_PAYLOAD_ID " ) ;
if ( ! pid ) pid = " " ;
fprintf ( f , " { \" sink_callee \" : \" %s \" , \" args \" :[ " , sink_callee ) ;
va_list ap ;
va_start ( ap , nargs ) ;
2026-05-14 13:10:22 -05:00
const char * args_arr [ 32 ] ;
int captured = nargs > 32 ? 32 : nargs ;
2026-05-14 05:35:28 -05:00
for ( int i = 0 ; i < nargs ; + + i ) {
const char * arg = va_arg ( ap , const char * ) ;
if ( ! arg ) arg = " " ;
2026-05-14 13:10:22 -05:00
if ( i < captured ) args_arr [ i ] = arg ;
2026-05-14 05:35:28 -05:00
if ( i > 0 ) fputc ( ',' , f ) ;
fprintf ( f , " { \" kind \" : \" String \" , \" value \" : \" %s \" } " , arg ) ;
}
va_end ( ap ) ;
2026-05-14 13:10:22 -05:00
fprintf ( f , " ], \" captured_at_ns \" :%llu, \" payload_id \" : \" %s \" , " , ns , pid ) ;
fputs ( " \" kind \" :{ \" kind \" : \" Normal \" }, \" witness \" : " , f ) ;
__nyx_write_witness ( f , sink_callee , captured , args_arr ) ;
fputs ( " } \n " , f ) ;
2026-05-14 05:35:28 -05:00
fclose ( f ) ;
}
2026-05-14 13:10:22 -05:00
/* Phase 08: sink-site signal handler. __nyx_install_crash_guard sets a
* sigaction ( 2 ) handler over SIGSEGV / SIGABRT / SIGBUS / SIGFPE / SIGILL
* that writes a Crash probe with witness before restoring SIG_DFL and
* re - raising the signal — the process still dies with the same exit
* code , but the probe channel now carries the forensic record . * /
static const char * __nyx_crash_sink_callee = " " ;
static void __nyx_crash_handler ( int sig ) {
const char * p = getenv ( " NYX_PROBE_PATH " ) ;
if ( p & & * p ) {
FILE * f = fopen ( p , " a " ) ;
if ( f ) {
const char * name = " SIGABRT " ;
switch ( sig ) {
case SIGSEGV : name = " SIGSEGV " ; break ;
case SIGABRT : name = " SIGABRT " ; break ;
case SIGBUS : name = " SIGBUS " ; break ;
case SIGFPE : name = " SIGFPE " ; break ;
case SIGILL : name = " SIGILL " ; break ;
}
struct timespec ts ;
clock_gettime ( CLOCK_REALTIME , & ts ) ;
unsigned long long ns = ( unsigned long long ) ts . tv_sec * 1000000000 ULL +
( unsigned long long ) ts . tv_nsec ;
const char * pid = getenv ( " NYX_PAYLOAD_ID " ) ;
if ( ! pid ) pid = " " ;
fprintf ( f ,
" { \" sink_callee \" : \" %s \" , \" args \" :[], \" captured_at_ns \" :%llu, "
" \" payload_id \" : \" %s \" , \" kind \" :{ \" kind \" : \" Crash \" , \" signal \" : \" %s \" }, "
" \" witness \" : " ,
__nyx_crash_sink_callee , ns , pid , name ) ;
__nyx_write_witness ( f , __nyx_crash_sink_callee , 0 , NULL ) ;
fputs ( " } \n " , f ) ;
fclose ( f ) ;
}
}
struct sigaction dfl ;
memset ( & dfl , 0 , sizeof ( dfl ) ) ;
dfl . sa_handler = SIG_DFL ;
sigaction ( sig , & dfl , NULL ) ;
raise ( sig ) ;
}
static void __nyx_install_crash_guard ( const char * sink_callee ) {
__nyx_crash_sink_callee = sink_callee ;
struct sigaction sa ;
memset ( & sa , 0 , sizeof ( sa ) ) ;
sa . sa_handler = __nyx_crash_handler ;
sigemptyset ( & sa . sa_mask ) ;
int sigs [ ] = { SIGSEGV , SIGABRT , SIGBUS , SIGFPE , SIGILL } ;
for ( size_t i = 0 ; i < sizeof ( sigs ) / sizeof ( sigs [ 0 ] ) ; + + i ) {
sigaction ( sigs [ i ] , & sa , NULL ) ;
}
}
2026-05-14 05:35:28 -05:00
" #
}
2026-05-14 03:22:30 -05:00
impl LangEmitter for CEmitter {
fn emit ( & self , _spec : & HarnessSpec ) -> Result < HarnessSource , UnsupportedReason > {
Err ( UnsupportedReason ::LangUnsupported )
}
fn entry_kinds_supported ( & self ) -> & 'static [ EntryKind ] {
SUPPORTED
}
fn entry_kind_hint ( & self , attempted : EntryKind ) -> String {
format! (
" c emitter is a stub; once Phase 16 (Track B Rust + C/C++ vertical) lands it will support {SUPPORTED:?} plus libFuzzer + main(argc, argv) shapes — attempted `EntryKind::{attempted}` "
)
}
}
#[ cfg(test) ]
mod tests {
use super ::* ;
#[ test ]
fn entry_kinds_supported_is_non_empty ( ) {
assert! ( ! CEmitter . entry_kinds_supported ( ) . is_empty ( ) ) ;
}
#[ test ]
fn entry_kind_hint_names_attempted_and_phase ( ) {
let hint = CEmitter . entry_kind_hint ( EntryKind ::LibraryApi ) ;
assert! ( hint . contains ( " LibraryApi " ) ) ;
assert! ( hint . contains ( " Phase 16 " ) ) ;
}
}