@@ -497,8 +497,24 @@ int vm_eval(JCC *vm) {
497497 else if (op == JMP ) { vm -> pc = (long long * )* vm -> pc ; } // jump to the address
498498 else if (op == JZ ) { vm -> pc = vm -> ax ? vm -> pc + 1 : (long long * )* vm -> pc ; } // jump if ax is zero
499499 else if (op == JNZ ) { vm -> pc = vm -> ax ? (long long * )* vm -> pc : vm -> pc + 1 ; } // jump if ax is not zero
500- else if (op == CALL ) { * -- vm -> sp = (long long )(vm -> pc + 1 ); vm -> pc = (long long * )* vm -> pc ; } // call subroutine
501- else if (op == CALLI ) { * -- vm -> sp = (long long )vm -> pc ; vm -> pc = (long long * )vm -> ax ; } // call subroutine indirect (address in ax)
500+ else if (op == CALL ) {
501+ // Call subroutine: push return address to main stack and shadow stack
502+ long long ret_addr = (long long )(vm -> pc + 1 );
503+ * -- vm -> sp = ret_addr ;
504+ if (vm -> enable_cfi ) {
505+ * -- vm -> shadow_sp = ret_addr ; // Also push to shadow stack for CFI
506+ }
507+ vm -> pc = (long long * )* vm -> pc ;
508+ }
509+ else if (op == CALLI ) {
510+ // Call subroutine indirect: push return address to main stack and shadow stack
511+ long long ret_addr = (long long )vm -> pc ;
512+ * -- vm -> sp = ret_addr ;
513+ if (vm -> enable_cfi ) {
514+ * -- vm -> shadow_sp = ret_addr ; // Also push to shadow stack for CFI
515+ }
516+ vm -> pc = (long long * )vm -> ax ;
517+ }
502518 else if (op == ENT ) {
503519 // Enter function: create new stack frame
504520 * -- vm -> sp = (long long )vm -> bp ; // Save old base pointer
@@ -580,9 +596,12 @@ int vm_eval(JCC *vm) {
580596 }
581597
582598 vm -> bp = (long long * )* vm -> sp ++ ; // Restore old base pointer
599+
600+ // Get return address from main stack
583601 vm -> pc = (long long * )* vm -> sp ++ ; // Restore program counter (return address)
584602
585603 // Check if we've returned from main (pc is NULL sentinel)
604+ // Skip CFI check for main's return since it was never called
586605 if (vm -> pc == 0 || vm -> pc == NULL ) {
587606 // Main has returned, exit with return value in ax
588607 int ret_val = (int )vm -> ax ;
@@ -592,6 +611,25 @@ int vm_eval(JCC *vm) {
592611
593612 return ret_val ;
594613 }
614+
615+ // CFI check: validate return address against shadow stack
616+ // Only for normal function returns (not main's exit)
617+ if (vm -> enable_cfi ) {
618+ long long shadow_ret_addr = * vm -> shadow_sp ++ ; // Pop return address from shadow stack
619+ long long main_ret_addr = (long long )vm -> pc ; // Return address we just loaded
620+
621+ if (main_ret_addr != shadow_ret_addr ) {
622+ printf ("\n========== CFI VIOLATION ==========\n" );
623+ printf ("Control flow integrity violation detected!\n" );
624+ printf ("Expected return address: 0x%llx\n" , shadow_ret_addr );
625+ printf ("Actual return address: 0x%llx\n" , main_ret_addr );
626+ printf ("Current PC offset: %lld\n" ,
627+ (long long )(vm -> pc - vm -> text_seg ));
628+ printf ("This indicates a ROP attack or stack corruption.\n" );
629+ printf ("====================================\n" );
630+ return -1 ; // Abort execution
631+ }
632+ }
595633 }
596634 else if (op == LEA ) {
597635 // Load effective address (bp + offset)
@@ -2255,6 +2293,10 @@ void cc_init(JCC *vm, bool enable_debugger) {
22552293 vm -> ptr_tags .buckets = NULL ;
22562294 vm -> ptr_tags .used = 0 ;
22572295
2296+ // Initialize CFI shadow stack (will be allocated if enable_cfi is set)
2297+ vm -> shadow_stack = NULL ;
2298+ vm -> shadow_sp = NULL ;
2299+
22582300 // Initialize segregated free lists
22592301 for (int i = 0 ; i < 12 ; i ++ ) { // NUM_SIZE_CLASSES
22602302 vm -> size_class_lists [i ] = NULL ;
@@ -2297,6 +2339,8 @@ void cc_destroy(JCC *vm) {
22972339 free (vm -> stack_seg );
22982340 if (vm -> heap_seg )
22992341 free (vm -> heap_seg );
2342+ if (vm -> shadow_stack )
2343+ free (vm -> shadow_stack );
23002344 // return_buffer is part of data_seg, no need to free separately
23012345
23022346 // Free init_state HashMap (string keys, no values to free)
@@ -2673,7 +2717,12 @@ int cc_run(JCC *vm, int argc, char **argv) {
26732717 // Setup stack
26742718 vm -> sp = (long long * )((char * )vm -> stack_seg + vm -> poolsize * sizeof (long long ));
26752719 vm -> bp = vm -> sp ; // Initialize base pointer to top of stack
2676-
2720+
2721+ // Setup shadow stack for CFI if enabled
2722+ if (vm -> enable_cfi ) {
2723+ vm -> shadow_sp = (long long * )((char * )vm -> shadow_stack + vm -> poolsize * sizeof (long long ));
2724+ }
2725+
26772726 // Save initial stack/base pointers for exit detection in vm_eval
26782727 vm -> initial_sp = vm -> sp ;
26792728 vm -> initial_bp = vm -> bp ;
0 commit comments