22e490e7 |
1 | diff -r -u src/src/syscall/chain.c src_set_syscall_workaround/src/syscall/chain.c |
2 | --- src/src/syscall/chain.c 2015-07-23 21:50:10.000000000 +0200 |
3 | +++ src_set_syscall_workaround/src/syscall/chain.c 2016-08-12 19:33:13.920471000 +0200 |
4 | @@ -39,17 +39,10 @@ |
5 | |
6 | STAILQ_HEAD(chained_syscalls, chained_syscall); |
7 | |
8 | -/** |
9 | - * Append a new syscall (@sysnum, @sysarg_*) to the list of |
10 | - * "unrequested" syscalls for the given @tracee. These new syscalls |
11 | - * will be triggered in order once the current syscall is done. The |
12 | - * caller is free to force the last result of this syscall chain in |
13 | - * @tracee->chain.final_result. This function returns -errno if an |
14 | - * error occurred, otherwise 0. |
15 | - */ |
16 | -int register_chained_syscall(Tracee *tracee, Sysnum sysnum, |
17 | +static int register_chained_syscall_internal(Tracee *tracee, Sysnum sysnum, |
18 | word_t sysarg_1, word_t sysarg_2, word_t sysarg_3, |
19 | - word_t sysarg_4, word_t sysarg_5, word_t sysarg_6) |
20 | + word_t sysarg_4, word_t sysarg_5, word_t sysarg_6, |
21 | + bool at_front) |
22 | { |
23 | struct chained_syscall *syscall; |
24 | |
25 | @@ -73,12 +66,35 @@ |
26 | syscall->sysargs[4] = sysarg_5; |
27 | syscall->sysargs[5] = sysarg_6; |
28 | |
29 | - STAILQ_INSERT_TAIL(tracee->chain.syscalls, syscall, link); |
30 | + if (at_front) { |
31 | + STAILQ_INSERT_HEAD(tracee->chain.syscalls, syscall, link); |
32 | + } else { |
33 | + STAILQ_INSERT_TAIL(tracee->chain.syscalls, syscall, link); |
34 | + } |
35 | |
36 | return 0; |
37 | } |
38 | |
39 | /** |
40 | + * Append a new syscall (@sysnum, @sysarg_*) to the list of |
41 | + * "unrequested" syscalls for the given @tracee. These new syscalls |
42 | + * will be triggered in order once the current syscall is done. The |
43 | + * caller is free to force the last result of this syscall chain in |
44 | + * @tracee->chain.final_result. This function returns -errno if an |
45 | + * error occurred, otherwise 0. |
46 | + */ |
47 | +int register_chained_syscall(Tracee *tracee, Sysnum sysnum, |
48 | + word_t sysarg_1, word_t sysarg_2, word_t sysarg_3, |
49 | + word_t sysarg_4, word_t sysarg_5, word_t sysarg_6) { |
50 | + return register_chained_syscall_internal( |
51 | + tracee, sysnum, |
52 | + sysarg_1, sysarg_2, sysarg_3, |
53 | + sysarg_4, sysarg_5, sysarg_6, |
54 | + false |
55 | + ); |
56 | +} |
57 | + |
58 | +/** |
59 | * Use/remove the first element of @tracee->chain.syscalls to forge a |
60 | * new syscall. This function should be called only at the end of in |
61 | * the sysexit stage. |
62 | @@ -126,6 +142,9 @@ |
63 | /* Move the instruction pointer back to the original trap. */ |
64 | instr_pointer = peek_reg(tracee, CURRENT, INSTR_POINTER); |
65 | poke_reg(tracee, INSTR_POINTER, instr_pointer - SYSTRAP_SIZE); |
66 | + |
67 | + /* Break after exit from syscall, there may be another one in chain */ |
68 | + tracee->restart_how = PTRACE_SYSCALL; |
69 | } |
70 | |
71 | /** |
72 | @@ -154,3 +173,18 @@ |
73 | peek_reg(tracee, ORIGINAL, SYSARG_5), |
74 | peek_reg(tracee, ORIGINAL, SYSARG_6)); |
75 | } |
76 | + |
77 | +int restart_current_syscall_as_chained(Tracee *tracee) |
78 | +{ |
79 | + assert(tracee->chain.sysnum_workaround_state == SYSNUM_WORKAROUND_INACTIVE); |
80 | + tracee->chain.sysnum_workaround_state = SYSNUM_WORKAROUND_PROCESS_FAULTY_CALL; |
81 | + return register_chained_syscall_internal(tracee, |
82 | + get_sysnum(tracee, CURRENT), |
83 | + peek_reg(tracee, CURRENT, SYSARG_1), |
84 | + peek_reg(tracee, CURRENT, SYSARG_2), |
85 | + peek_reg(tracee, CURRENT, SYSARG_3), |
86 | + peek_reg(tracee, CURRENT, SYSARG_4), |
87 | + peek_reg(tracee, CURRENT, SYSARG_5), |
88 | + peek_reg(tracee, CURRENT, SYSARG_6), |
89 | + true); |
90 | +} |
91 | diff -r -u src/src/syscall/chain.h src_set_syscall_workaround/src/syscall/chain.h |
92 | --- src/src/syscall/chain.h 2015-07-23 21:50:10.000000000 +0200 |
93 | +++ src_set_syscall_workaround/src/syscall/chain.h 2016-08-09 17:12:36.448471000 +0200 |
94 | @@ -37,5 +37,7 @@ |
95 | |
96 | extern void chain_next_syscall(Tracee *tracee); |
97 | |
98 | +extern int restart_current_syscall_as_chained(Tracee *tracee); |
99 | + |
100 | |
101 | #endif /* CHAIN_H */ |
102 | diff -r -u src/src/syscall/syscall.c src_set_syscall_workaround/src/syscall/syscall.c |
103 | --- src/src/syscall/syscall.c 2015-07-23 21:50:10.000000000 +0200 |
104 | +++ src_set_syscall_workaround/src/syscall/syscall.c 2016-08-12 19:32:35.199527000 +0200 |
105 | @@ -31,6 +31,7 @@ |
106 | #include "tracee/tracee.h" |
107 | #include "tracee/reg.h" |
108 | #include "tracee/mem.h" |
109 | +#include "cli/note.h" |
110 | |
111 | /** |
112 | * Copy in @path a C string (PATH_MAX bytes max.) from the @tracee's |
113 | @@ -126,7 +127,9 @@ |
114 | save_current_regs(tracee, MODIFIED); |
115 | } |
116 | else { |
117 | - status = notify_extensions(tracee, SYSCALL_CHAINED_ENTER, 0, 0); |
118 | + if (tracee->chain.sysnum_workaround_state != SYSNUM_WORKAROUND_PROCESS_REPLACED_CALL) { |
119 | + status = notify_extensions(tracee, SYSCALL_CHAINED_ENTER, 0, 0); |
120 | + } |
121 | tracee->restart_how = PTRACE_SYSCALL; |
122 | } |
123 | |
124 | @@ -159,8 +162,13 @@ |
125 | /* Translate the syscall only if it was actually |
126 | * requested by the tracee, it is not a syscall |
127 | * chained by PRoot. */ |
128 | - if (tracee->chain.syscalls == NULL) |
129 | + if (tracee->chain.syscalls == NULL || tracee->chain.sysnum_workaround_state == SYSNUM_WORKAROUND_PROCESS_REPLACED_CALL) { |
130 | + tracee->chain.sysnum_workaround_state = SYSNUM_WORKAROUND_INACTIVE; |
131 | translate_syscall_exit(tracee); |
132 | + } |
133 | + else if (tracee->chain.sysnum_workaround_state == SYSNUM_WORKAROUND_PROCESS_FAULTY_CALL) { |
134 | + tracee->chain.sysnum_workaround_state = SYSNUM_WORKAROUND_PROCESS_REPLACED_CALL; |
135 | + } |
136 | else |
137 | (void) notify_extensions(tracee, SYSCALL_CHAINED_EXIT, 0, 0); |
138 | |
139 | @@ -172,7 +180,42 @@ |
140 | chain_next_syscall(tracee); |
141 | } |
142 | |
143 | - (void) push_regs(tracee); |
144 | + bool override_sysnum = is_enter_stage && tracee->chain.syscalls == NULL; |
145 | + int push_regs_status = push_specific_regs(tracee, override_sysnum); |
146 | + |
147 | + /* Handle inability to change syscall number */ |
148 | + if (push_regs_status < 0 && override_sysnum) { |
149 | + word_t orig_sysnum = peek_reg(tracee, ORIGINAL, SYSARG_NUM); |
150 | + word_t current_sysnum = peek_reg(tracee, CURRENT, SYSARG_NUM); |
151 | + if (orig_sysnum != current_sysnum) { |
152 | + /* Restart current syscall as chained */ |
153 | + if (current_sysnum != SYSCALL_AVOIDER) { |
154 | + restart_current_syscall_as_chained(tracee); |
155 | + } |
156 | + |
157 | + /* Set syscall arguments to make it fail |
158 | + * TODO: More reliable way to make invalid arguments */ |
159 | + if (get_sysnum(tracee, ORIGINAL) == PR_brk) { |
160 | + /* For brk() we pass 0 as first arg; this is used to query value without changing it */ |
161 | + poke_reg(tracee, SYSARG_1, 0); |
162 | + } else { |
163 | + /* For other syscalls we set all args to -1 |
164 | + * Hoping there is among them invalid request/address/fd/value that will make syscall fail */ |
165 | + poke_reg(tracee, SYSARG_1, -1); |
166 | + poke_reg(tracee, SYSARG_2, -1); |
167 | + poke_reg(tracee, SYSARG_3, -1); |
168 | + poke_reg(tracee, SYSARG_4, -1); |
169 | + poke_reg(tracee, SYSARG_5, -1); |
170 | + poke_reg(tracee, SYSARG_6, -1); |
171 | + } |
172 | + |
173 | + /* Push regs again without changing syscall */ |
174 | + push_regs_status = push_specific_regs(tracee, false); |
175 | + if (push_regs_status != 0) { |
176 | + note(tracee, WARNING, SYSTEM, "can't set tracee registers in workaround"); |
177 | + } |
178 | + } |
179 | + } |
180 | |
181 | if (is_enter_stage) |
182 | print_current_regs(tracee, 5, "sysenter end" ); |
183 | diff -r -u src/src/tracee/reg.c src_set_syscall_workaround/src/tracee/reg.c |
184 | --- src/src/tracee/reg.c 2015-07-23 21:50:10.000000000 +0200 |
185 | +++ src_set_syscall_workaround/src/tracee/reg.c 2016-08-12 14:48:31.410423000 +0200 |
186 | @@ -262,12 +262,7 @@ |
187 | return 0; |
188 | } |
189 | |
190 | -/** |
191 | - * Copy the cached values of all @tracee's general purpose registers |
192 | - * back to the process, if necessary. This function returns -errno if |
193 | - * an error occured, 0 otherwise. |
194 | - */ |
195 | -int push_regs(Tracee *tracee) |
196 | +int push_specific_regs(Tracee *tracee, bool including_sysnum) |
197 | { |
198 | int status; |
199 | |
200 | @@ -306,12 +301,14 @@ |
201 | /* Update syscall number if needed. On arm64, a new |
202 | * subcommand has been added to PTRACE_{S,G}ETREGSET |
203 | * to allow write/read of current sycall number. */ |
204 | - if (current_sysnum != REG(tracee, ORIGINAL, SYSARG_NUM)) { |
205 | + if (including_sysnum && current_sysnum != REG(tracee, ORIGINAL, SYSARG_NUM)) { |
206 | regs.iov_base = ¤t_sysnum; |
207 | regs.iov_len = sizeof(current_sysnum); |
208 | status = ptrace(PTRACE_SETREGSET, tracee->pid, NT_ARM_SYSTEM_CALL, ®s); |
209 | - if (status < 0) |
210 | - note(tracee, WARNING, SYSTEM, "can't set the syscall number"); |
211 | + if (status < 0) { |
212 | + //note(tracee, WARNING, SYSTEM, "can't set the syscall number"); |
213 | + return status; |
214 | + } |
215 | } |
216 | |
217 | /* Update other registers. */ |
218 | @@ -325,10 +322,12 @@ |
219 | * change effectively the syscall number during a |
220 | * ptrace-stop. */ |
221 | word_t current_sysnum = REG(tracee, CURRENT, SYSARG_NUM); |
222 | - if (current_sysnum != REG(tracee, ORIGINAL, SYSARG_NUM)) { |
223 | + if (including_sysnum && current_sysnum != REG(tracee, ORIGINAL, SYSARG_NUM)) { |
224 | status = ptrace(PTRACE_SET_SYSCALL, tracee->pid, 0, current_sysnum); |
225 | - if (status < 0) |
226 | - note(tracee, WARNING, SYSTEM, "can't set the syscall number"); |
227 | + if (status < 0) { |
228 | + //note(tracee, WARNING, SYSTEM, "can't set the syscall number"); |
229 | + return status; |
230 | + } |
231 | } |
232 | # endif |
233 | |
234 | @@ -340,3 +339,12 @@ |
235 | |
236 | return 0; |
237 | } |
238 | + |
239 | +/** |
240 | + * Copy the cached values of all @tracee's general purpose registers |
241 | + * back to the process, if necessary. This function returns -errno if |
242 | + * an error occured, 0 otherwise. |
243 | + */ |
244 | +int push_regs(Tracee *tracee) { |
245 | + return push_specific_regs(tracee, true); |
246 | +} |
247 | diff -r -u src/src/tracee/reg.h src_set_syscall_workaround/src/tracee/reg.h |
248 | --- src/src/tracee/reg.h 2015-07-23 21:50:10.000000000 +0200 |
249 | +++ src_set_syscall_workaround/src/tracee/reg.h 2016-08-09 21:38:03.863456000 +0200 |
250 | @@ -43,6 +43,7 @@ |
251 | } Reg; |
252 | |
253 | extern int fetch_regs(Tracee *tracee); |
254 | +extern int push_specific_regs(Tracee *tracee, bool including_sysnum); |
255 | extern int push_regs(Tracee *tracee); |
256 | |
257 | extern word_t peek_reg(const Tracee *tracee, RegVersion version, Reg reg); |
258 | diff -r -u src/src/tracee/tracee.h src_set_syscall_workaround/src/tracee/tracee.h |
259 | --- src/src/tracee/tracee.h 2016-08-12 19:44:07.301407472 +0200 |
260 | +++ src_set_syscall_workaround/src/tracee/tracee.h 2016-08-12 19:52:43.554712737 +0200 |
261 | @@ -193,6 +193,11 @@ |
262 | struct chained_syscalls *syscalls; |
263 | bool force_final_result; |
264 | word_t final_result; |
265 | + enum { |
266 | + SYSNUM_WORKAROUND_INACTIVE, |
267 | + SYSNUM_WORKAROUND_PROCESS_FAULTY_CALL, |
268 | + SYSNUM_WORKAROUND_PROCESS_REPLACED_CALL |
269 | + } sysnum_workaround_state; |
270 | } chain; |
271 | |
272 | /* Load info generated during execve sysenter and used during |