# JITScanner: Just-in-Time Executable Page Check in the Linux Operating System +
## 3 The JITScanner System
### Executable-Page Access Interception
These bits store the original write and execute permissions for a specific page before any modifications by our module. During the interception of a fault, we check these bits within the PTE. If the fault originates from a genuine invalid access generated by the program and not by our shadow state machine, the bits help us identify this distinction. In such cases, we allow the kernel to handle the fault without any intervention from our module. This mechanism ensures that our system effectively identifies and manages faults triggered by the shadow state machine while allowing standard invalid-access faults to be handled conventionally by the kernel. We present in Listings 1 and 2 the main parts related to the hook-based page management process. In Listing 1, we illustrate the management of our state machine in the handle_mm_fault() hook, which is invoked on page materialization and a write access violation. The steps are as follows: first, we do some general checks to ensure that the fault occurred in a valid vm_area with the appropriate flags. We then examine our ORIG_WRITE_BIT bit in the page table, to verify if the page has been modified by our state machine and if writes were initially permitted, if so, we disable execution and allow the write. Otherwise, we let the kernel handle the fault conventionally. In Listing 2, we report the hook for force_sig_fault(), which is called on an execute-address violation. Here, we check if our ORIG_EXE_BIT bit is set in the page table. If it is not set, we let the kernel handle the fault. If it is set, this fault is generated under our state machine and needs to be resolved. To this end, we first run a synchronous check of the page content at kernel level. If a threat is detected, we allow the kernel to complete the fault, resulting in the delivery of the SIGKILL signal to the running process. If no threat is found during the kernel-levelcheck, we transfer the page snapshot to the user agent for asynchronous checking and then fix the page table to allow the execution to continue. The necessity of a TLB (Translation Lookaside Buffer) flush within the code listings is crucial due to the way TLBs maintain information based on the previous access permissions of a page. When the permissions of a specific page are modified, it becomes imperative to eliminate this page's address from the TLB across all CPUs involved in executing threads operating within the same address space as the intercepted thread accessing memory. In our implementation, we have leveraged the __flush_tlb_one_user API provided by the Linux kernel for this purpose. This API allows us to perform a TLB flush for a specific page address. Additionally, we combine this API with the Inter-Processor-Interrupt (IPI) approach to execute the function across all CPUs involved. This approach ensures that the TLBs across all relevant CPUs are updated and purged of the outdated page information, aligning them with the modified access permissions for the page. ```
if(general_checks()==OK&& pte[fault_page]&ORIG_WRITE_BIT){ set_bit(fault_page,XD_BIT); set_bit(fault_page,W_BIT); set_bit(fault_page,ORIG_EXE_BIT); flush_tlb(fault_page); goto_allow_write; }else{ goto_standard_fault_handler; }
```  
Listing 1: The hook on handle_mm_fault(). ```
if(pte[rip_page]&ORIG_EXE_BIT){ if(sync_check(rip_page)!=OK)gotosend_sig_kill; transfer_page_to_user(rip_page); clear_bit(rip_page,XD_BIT); clear_bit(rip_page,W_BIT); set_bit(fault_page,ORIG_WRITE_BIT); flush_tlb(fault_page); goto_allow_normal_execution; }else{ goto_standard_fault_handler; }
```  
Listing 2: The hook on force_sig_fault(). Finally, installing hooks on system calls such as mprotect() is a significant step within our system. This particular system call is capable of altering the access permissions to a page, such as adding executable (X) or writable (W) permissions to a page that previously had different permissions.