chiark / gitweb /
a5ac0d4f67b17d16f3568096b35ed7a5699a5506
[termux-packages] / packages / proot / workaround-NT_ARM_SYSTEM_CALL.patch
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 = &current_sysnum;
207                         regs.iov_len = sizeof(current_sysnum);
208                         status = ptrace(PTRACE_SETREGSET, tracee->pid, NT_ARM_SYSTEM_CALL, &regs);
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