Please explain why vm_shared_region_map_file is calling vm_shared_region_undo_mappings with an incorrect srf_current_mappings_count argument.
The bug occurs due to a mismatch between the caller's argument interpretation and the parameter design of vm_shared_region.c, which is exacerbated by confusing
parameter names in the function declaration.
──────
In vm_shared_region.c, when a slide relocation fails in vm_shared_region_map_file , the function attempts to clean up all previously established mappings by calling vm_shared_region_undo_mappings :
vm_shared_region_undo_mappings(shared_region,
sr_map, shared_region->sr_base_address,
&sr_file_mappings[0],
&sr_file_mappings[sr_file_mappings_count - 1],
sr_file_mappings_count); // <-- INCORRECT ARGUMENT
The last argument is supposed to represent the number of segment/section mappings (individual mappings) within the last file mapping ( srf_mappings_current /
sr_file_mappings[sr_file_mappings_count - 1] ) to undo.
However, the caller incorrectly passes sr_file_mappings_count , which is the total number of files/vnodes mapped (i.e., the size of the sr_file_mappings
array, typically 1 or 2 ).
Instead of sr_file_mappings_count , the correct argument should be:
sr_file_mappings[sr_file_mappings_count - 1].mappings_count
The developer who wrote the incorrect call was likely misled by the function's declaration in vm_shared_region_internal.h:
extern void vm_shared_region_undo_mappings(
struct vm_shared_region *shared_region,
vm_map_t sr_map,
mach_vm_offset_t sr_base_address,
struct _sr_file_mappings *srf_mappings,
struct _sr_file_mappings *srf_mappings_count, // Parameter 5
unsigned int mappings_count); // Parameter 6
In the header:
• Parameter 5 is named srf_mappings_count but is actually a pointer to the current file mapping ( srf_mappings_current in the implementation).
• Parameter 6 is named mappings_count but is actually the current file's mapping count ( srf_current_mappings_count in the implementation).
This confusing naming led the caller to mistakenly pass the array size sr_file_mappings_count to Parameter 6.
When vm_shared_region.c is executing:
if (srfmp == srf_mappings_current) {
mappings_count = srf_current_mappings_count; // Set to sr_file_mappings_count
}
• If sr_file_mappings_count < srf_mappings_current->mappings_count (Typical Case):
Only the first few mappings of the last file are undone. The remaining successfully mapped segments of that file are left mapped, causing a resource leak and
potential overlap conflicts during subsequent attempts.
• If sr_file_mappings_count > srf_mappings_current->mappings_count :
The j < mappings_count loop will execute out of bounds of the srfmp->mappings array, accessing undefined kernel memory and potentially triggering a kernel
panic.
No, this bug does not lead to direct kernel memory corruption, but it has a high probability of causing a kernel panic (crash) or a denial of service (user-space crash).
Here is a breakdown of why kernel memory is not corrupted and what the actual consequences are:
──────
- Read-Only Out-of-Bounds Access: The loop in vm_shared_region.c only reads from the mappings array (e.g., checking mappings[j].sms_size and calculating start / end ). It never writes back to the array or any out-of-bounds heap memory.
- User-Space Target Map: The target map passed to vm_map.c is sr_map , which represents the shared region's submap. This is a user-space map (used for
the shared cache) and does not contain or overlap with kernel-space memory (like the kernel heap, stacks, or kernel code). Removing mappings from this map does
not alter or corrupt kernel-space state.
──────
Instead of silent corruption, reading garbage values out of bounds is highly likely to panic the system in one of two ways:
- Misaligned Address Panic:
In vm_map_delete_and_iunlock_internal , the kernel enforces that the deletion boundaries must be page-aligned:
if (start & VM_MAP_PAGE_MASK(map)) {
__vm_map_delete_misaligned_panic(map, start, end);
}
Because the out-of-bounds memory read contains arbitrary heap garbage, the computed start address is almost certain to not be page-aligned, immediately
triggering vm_map.c and crashing the kernel. - Kernel Page Fault:
If the mappings array (allocated via kalloc_data ) happens to be positioned at the end of a page boundary and the next virtual page is unmapped (e.g., a guard page), the out-of-bounds read itself will trigger a kernel page fault, resulting in a system panic.
──────
In the very unlikely event that the garbage values from the heap happen to be page-aligned and yield addresses within the valid boundaries of sr_map :
• vm_map.c will succeed in deleting those pages from sr_map .
• Since the shared region's map is shared across processes, this would unmap portions of the shared cache (like system libraries) for user-space applications.
• Any application subsequently trying to access these unmapped pages will crash with a segmentation fault ( EXC_BAD_ACCESS ), leading to a system-wide user-space
denial of service.