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