• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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