1 /*
2 * Copyright (C) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <signal.h>
19 #include <pthread.h>
20 #include <fcntl.h>
21 #include <stdint.h>
22 #include <string.h>
23 #include <sys/wait.h>
24 #include <unistd.h>
25 #include <musl_log.h>
26 #include <atomic.h>
27 #include <dirent.h>
28 #include <sys/types.h>
29 #include <errno.h>
30
31 #include "functionalext.h"
32
33 #define DEV_NULL_PATH "/dev/null"
34 #define DEBUG_SIGNAL (42)
35 #define ONE (1)
36 #define ZERO (0)
37 #define TWO (2)
38 #define THREE (3)
39 #define DIRECTORY_PATH "/data/log/faultlog/temp"
40 #define FDSAN_SEARCH_STRING "DEBUG SIGNAL(FDSAN)"
41 #define BADFD_SEARCH_STRING "EBADF:"
42
43 #define TEMP_FD_0 (0)
44 #define TEMP_FD_1 (1)
45 #define TEMP_FD_2 (2)
46 #define TEMP_FD_3 (3)
47 #define TEMP_FD_4 (4)
48 #define TEMP_FD_5 (5)
49 #define TEMP_FD_6 (6)
50 #define INVALID_TAG_999 (999)
51 #define INVALID_TAG_998 (998)
52 #define TEMP_TAG_99 (99)
53
54 // 子线程函数,向主线程发送信号,触发fdsan校验失败循环三次
thread_func(void * arg)55 void* thread_func(void* arg)
56 {
57 for (int i = ZERO; i < THREE; i++) {
58 fdsan_exchange_owner_tag((int)arg, INVALID_TAG_999, INVALID_TAG_998);
59 }
60 return NULL;
61 }
62
63 #define MAX_PATH_LENGTH (1024)
64
65 #define NOT_FOUND (0)
66 #define FOUND (1)
67 // 函数:检查文件中是否包含目标字符串
contains_string(FILE * file,const char * search_string)68 int contains_string(FILE *file, const char *search_string)
69 {
70 char line[1024];
71 while (fgets(line, sizeof(line), file) != NULL) {
72 if (strstr(line, search_string) != NULL) {
73 return FOUND; // 找到目标字符串
74 }
75 }
76 return NOT_FOUND; // 没有找到
77 }
78
79 // 函数:遍历目录,检查文件
count_string_in_directory(const char * dir_path,const char * search_string1,const char * search_string2)80 int count_string_in_directory(const char *dir_path, const char *search_string1, const char *search_string2)
81 {
82 DIR *dir = opendir(dir_path);
83 if (dir == NULL) {
84 t_error("can not open dir");
85 return -1;
86 }
87
88 struct dirent *entry;
89 int count = ZERO;
90
91 // 遍历目录中的每个文件
92 while ((entry = readdir(dir)) != NULL) {
93 // 跳过 "." 和 ".."
94 if (strcmp(entry->d_name, ".") == ZERO || strcmp(entry->d_name, "..") == 0) {
95 continue;
96 }
97
98 // 构造文件的完整路径
99 char file_path[MAX_PATH_LENGTH];
100 (void)snprintf(file_path, sizeof(file_path), "%s/%s", dir_path, entry->d_name);
101
102 // 判断是否为文件(可以进一步扩展检查是否为普通文件)
103 FILE *file = fopen(file_path, "r");
104 if (file != NULL) {
105 // 如果文件中包含目标字符串,计数+1
106 if (contains_string(file, search_string1) && contains_string(file, search_string2)) {
107 count++;
108 }
109 fclose(file);
110 }
111 }
112
113 closedir(dir);
114 return count;
115 }
116
wait_until_signal(void)117 void wait_until_signal(void)
118 {
119 sigset_t set;
120 sigemptyset(&set);
121 sigaddset(&set, DEBUG_SIGNAL);
122 // 设置超时结构,设置超时为 0.5 秒
123 struct timespec timeout;
124 timeout.tv_sec = 0; // 秒部分为 0
125 timeout.tv_nsec = 500000000; // 纳秒部分为 500000000,即 0.5 秒
126 (void)sigtimedwait(&set, NULL, &timeout);
127 }
128
129 #define TEMP_LENGTH (256)
130 #define RIGHT_RESULT (1)
131
check_same_fd_same_time_fdsan_signal_once(void)132 void check_same_fd_same_time_fdsan_signal_once(void)
133 {
134 (void)system("rm -rf /data/log/faultlog/temp/*");
135 // 创建两个线程
136 pthread_t thread1, thread2;
137 int result = pthread_create(&thread1, NULL, thread_func, (void *)TEMP_FD_0);
138 if (result != 0) {
139 t_error("check_fdsan_signal_once pthread_create1 failed result=%d\n", result);
140 return;
141 }
142 result = pthread_create(&thread2, NULL, thread_func, (void *)TEMP_FD_0);
143 if (result != 0) {
144 t_error("check_fdsan_signal_once pthread_create2 failed result=%d\n", result);
145 return;
146 }
147
148 // 主线程等待子线程执行完
149 pthread_join(thread1, NULL);
150 pthread_join(thread2, NULL);
151
152 // 获取当前进程的 PID
153 pid_t pid = getpid();
154
155 // 用于存储拼接后的字符串
156 char search_string[TEMP_LENGTH];
157
158 // 使用 sprintf 将 PID 转换为字符串并与其他内容拼接
159 (void)sprintf(search_string, "Pid:%d", pid);
160
161 wait_until_signal();
162 // 检查生成了几个文件
163 result = count_string_in_directory(DIRECTORY_PATH, search_string, FDSAN_SEARCH_STRING);
164 if (result != RIGHT_RESULT) {
165 t_error("The target string '%s' appears in %d files\n", search_string, result);
166 }
167 }
168
check_same_fd_diff_time_fdsan_signal_once(void)169 void check_same_fd_diff_time_fdsan_signal_once(void)
170 {
171 (void)system("rm -rf /data/log/faultlog/temp/*");
172 fdsan_exchange_owner_tag(TEMP_FD_1, INVALID_TAG_999, INVALID_TAG_998);
173 fdsan_exchange_owner_tag(TEMP_FD_1, INVALID_TAG_999, INVALID_TAG_998);
174
175 // 获取当前进程的 PID
176 pid_t pid = getpid();
177
178 // 用于存储拼接后的字符串
179 char search_string[TEMP_LENGTH];
180
181 // 使用 sprintf 将 PID 转换为字符串并与其他内容拼接
182 (void)sprintf(search_string, "Pid:%d", pid);
183
184 wait_until_signal();
185 // 检查生成了几个文件
186 int result = count_string_in_directory(DIRECTORY_PATH, search_string, FDSAN_SEARCH_STRING);
187 if (result != RIGHT_RESULT) {
188 t_error("The target string '%s' appears in %d files\n", search_string, result);
189 }
190 }
191
check_diff_fd_same_time_fdsan_signal_twice(void)192 void check_diff_fd_same_time_fdsan_signal_twice(void)
193 {
194 (void)system("rm -rf /data/log/faultlog/temp/*");
195 // 创建两个线程
196 pthread_t thread1, thread2;
197 int result = pthread_create(&thread1, NULL, thread_func, (void *)TEMP_FD_2);
198 if (result != 0) {
199 t_error("check_fdsan_signal_once pthread_create1 failed result=%d\n", result);
200 return;
201 }
202 result = pthread_create(&thread2, NULL, thread_func, (void *)TEMP_FD_3);
203 if (result != 0) {
204 t_error("check_fdsan_signal_once pthread_create2 failed result=%d\n", result);
205 return;
206 }
207
208 // 主线程等待子线程执行完
209 pthread_join(thread1, NULL);
210 pthread_join(thread2, NULL);
211
212 // 获取当前进程的 PID
213 pid_t pid = getpid();
214
215 // 用于存储拼接后的字符串
216 char search_string[TEMP_LENGTH];
217
218 // 使用 sprintf 将 PID 转换为字符串并与其他内容拼接
219 (void)sprintf(search_string, "Pid:%d", pid);
220
221 wait_until_signal();
222 // 检查生成了几个文件
223 result = count_string_in_directory(DIRECTORY_PATH, search_string, FDSAN_SEARCH_STRING);
224 if (result != TWO) {
225 t_error("The target string '%s' appears in %d files\n", search_string, result);
226 }
227 }
228
check_diff_fd_diff_time_fdsan_signal_twice(void)229 void check_diff_fd_diff_time_fdsan_signal_twice(void)
230 {
231 (void)system("rm -rf /data/log/faultlog/temp/*");
232 fdsan_exchange_owner_tag(TEMP_FD_4, INVALID_TAG_999, INVALID_TAG_998);
233 fdsan_exchange_owner_tag(TEMP_FD_5, INVALID_TAG_999, INVALID_TAG_998);
234
235 // 获取当前进程的 PID
236 pid_t pid = getpid();
237
238 // 用于存储拼接后的字符串
239 char search_string[TEMP_LENGTH];
240
241 // 使用 sprintf 将 PID 转换为字符串并与其他内容拼接
242 (void)sprintf(search_string, "Pid:%d", pid);
243
244 wait_until_signal();
245 // 检查生成了几个文件
246 int result = count_string_in_directory(DIRECTORY_PATH, search_string, FDSAN_SEARCH_STRING);
247 if (result != TWO) {
248 t_error("The target string '%s' appears in %d files\n", search_string, result);
249 }
250 }
251
check_bad_fd(void)252 void check_bad_fd(void)
253 {
254 (void)system("rm -rf /data/log/faultlog/temp/*");
255 int fd = open("/data/local/tmp/newfile.txt", O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);
256 if (fd == -1) {
257 t_error("check_bad_fd open failed errno=%d\n", errno);
258 return;
259 }
260 fdsan_exchange_owner_tag(fd, ZERO, TEMP_TAG_99);
261 fdsan_close_with_tag(fd, TEMP_TAG_99);
262 fdsan_close_with_tag(fd, TEMP_TAG_99);
263 fdsan_close_with_tag(fd, TEMP_TAG_99);
264
265 // 获取当前进程的 PID
266 pid_t pid = getpid();
267
268 // 用于存储拼接后的字符串
269 char search_string[TEMP_LENGTH];
270
271 // 使用 sprintf 将 PID 转换为字符串并与其他内容拼接
272 (void)sprintf(search_string, "Pid:%d", pid);
273
274 wait_until_signal();
275 // 检查生成了几个文件
276 int result = count_string_in_directory(DIRECTORY_PATH, search_string, BADFD_SEARCH_STRING);
277 if (result == ZERO) {
278 t_error("The target string '%s' appears in %d files\n", search_string, result);
279 }
280 }
281
main(void)282 int main(void)
283 {
284 FILE *fp = popen("uname", "r");
285 char buffer[128];
286 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
287 printf("uname result %s", buffer);
288 // jump over this testcase
289 if (strstr(buffer, "Linux")) {
290 return ZERO;
291 }
292 }
293 // 1. 检查同fd,两线程同时触发校验,生成几次报告 预期1次
294 check_same_fd_same_time_fdsan_signal_once();
295 // 2. 检查同fd,前后触发校验,生成几次报告 预期1次
296 check_same_fd_diff_time_fdsan_signal_once();
297 // 3. 检查不同fd,同时触发校验,生成几次报告 预期2次
298 check_diff_fd_same_time_fdsan_signal_twice();
299 // 4. 检查不同fd,前后触发校验,生成几次报告 预期2次
300 check_diff_fd_diff_time_fdsan_signal_twice();
301 // 5. 检查EBADF触发42信号是否不受影响
302 check_bad_fd();
303 return t_status;
304 }
305