Skip to content

Commit 78e3e06

Browse files
committed
syscall: Use openat() instead of open()
Since sys_open is deprecated and some architectures don't support it. We switch the implementation to sys_openat. Moreover, in some architectures like x86-64, the prototype of syscall, for example, openat(), might have been changed to struct pt_regs [1] but we cannot promise that so support the two types (sys_openat and pt_regs). Also, to prevent other untraced tasks print out the information, add the uid checking in our_sys_openat(). [1] https://lore.kernel.org/lkml/[email protected]/ Close #159
1 parent 2f5fd9a commit 78e3e06

File tree

2 files changed

+47
-25
lines changed

2 files changed

+47
-25
lines changed

examples/syscall.c

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include <linux/module.h>
1616
#include <linux/moduleparam.h> /* which will have params */
1717
#include <linux/unistd.h> /* The list of system calls */
18+
#include <linux/cred.h> /* For current_uid() */
19+
#include <linux/uidgid.h> /* For __kuid_val() */
1820
#include <linux/version.h>
1921

2022
/* For the current (process) structure, we need this to know who the
@@ -62,22 +64,26 @@ module_param(sym, ulong, 0644);
6264
static unsigned long **sys_call_table;
6365

6466
/* UID we want to spy on - will be filled from the command line. */
65-
static int uid;
67+
static uid_t uid = -1;
6668
module_param(uid, int, 0644);
6769

6870
/* A pointer to the original system call. The reason we keep this, rather
69-
* than call the original function (sys_open), is because somebody else
71+
* than call the original function (sys_openat), is because somebody else
7072
* might have replaced the system call before us. Note that this is not
71-
* 100% safe, because if another module replaced sys_open before us,
73+
* 100% safe, because if another module replaced sys_openat before us,
7274
* then when we are inserted, we will call the function in that module -
7375
* and it might be removed before we are.
7476
*
75-
* Another reason for this is that we can not get sys_open.
77+
* Another reason for this is that we can not get sys_openat.
7678
* It is a static variable, so it is not exported.
7779
*/
78-
static asmlinkage int (*original_call)(const char __user *, int, umode_t);
80+
#ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER
81+
static asmlinkage long (*original_call)(const struct pt_regs *);
82+
#else
83+
static asmlinkage long (*original_call)(int, const char __user *, int, umode_t);
84+
#endif
7985

80-
/* The function we will replace sys_open (the function called when you
86+
/* The function we will replace sys_openat (the function called when you
8187
* call the open system call) with. To find the exact prototype, with
8288
* the number and type of arguments, we find the original function first
8389
* (it is at fs/open.c).
@@ -87,25 +93,41 @@ static asmlinkage int (*original_call)(const char __user *, int, umode_t);
8793
* wreck havoc and require programs to be recompiled, since the system
8894
* calls are the interface between the kernel and the processes).
8995
*/
90-
static asmlinkage int our_sys_open(const char __user *filename, int flags,
91-
umode_t mode)
96+
#ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER
97+
static asmlinkage long our_sys_openat(const struct pt_regs *regs)
98+
#else
99+
static asmlinkage long our_sys_openat(int dfd, const char __user *filename,
100+
int flags, umode_t mode)
101+
#endif
92102
{
93103
int i = 0;
94104
char ch;
95105

106+
if (__kuid_val(current_uid()) != uid)
107+
goto orig_call;
108+
96109
/* Report the file, if relevant */
97110
pr_info("Opened file by %d: ", uid);
98111
do {
112+
#ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER
113+
get_user(ch, (char __user *)regs->si + i);
114+
#else
99115
get_user(ch, (char __user *)filename + i);
116+
#endif
100117
i++;
101118
pr_info("%c", ch);
102119
} while (ch != 0);
103120
pr_info("\n");
104121

105-
/* Call the original sys_open - otherwise, we lose the ability to
122+
orig_call:
123+
/* Call the original sys_openat - otherwise, we lose the ability to
106124
* open files.
107125
*/
108-
return original_call(filename, flags, mode);
126+
#ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER
127+
return original_call(regs);
128+
#else
129+
return original_call(dfd, filename, flags, mode);
130+
#endif
109131
}
110132

111133
static unsigned long **acquire_sys_call_table(void)
@@ -192,10 +214,10 @@ static int __init syscall_start(void)
192214
disable_write_protection();
193215

194216
/* keep track of the original open function */
195-
original_call = (void *)sys_call_table[__NR_open];
217+
original_call = (void *)sys_call_table[__NR_openat];
196218

197-
/* use our open function instead */
198-
sys_call_table[__NR_open] = (unsigned long *)our_sys_open;
219+
/* use our openat function instead */
220+
sys_call_table[__NR_openat] = (unsigned long *)our_sys_openat;
199221

200222
enable_write_protection();
201223

@@ -210,15 +232,15 @@ static void __exit syscall_end(void)
210232
return;
211233

212234
/* Return the system call back to normal */
213-
if (sys_call_table[__NR_open] != (unsigned long *)our_sys_open) {
235+
if (sys_call_table[__NR_openat] != (unsigned long *)our_sys_openat) {
214236
pr_alert("Somebody else also played with the ");
215237
pr_alert("open system call\n");
216238
pr_alert("The system may be left in ");
217239
pr_alert("an unstable state.\n");
218240
}
219241

220242
disable_write_protection();
221-
sys_call_table[__NR_open] = (unsigned long *)original_call;
243+
sys_call_table[__NR_openat] = (unsigned long *)original_call;
222244
enable_write_protection();
223245

224246
msleep(2000);

lkmpg.tex

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,24 +1529,24 @@ \section{System Calls}
15291529

15301530
The source code here is an example of such a kernel module.
15311531
We want to ``spy'' on a certain user, and to \cpp|pr_info()| a message whenever that user opens a file.
1532-
Towards this end, we replace the system call to open a file with our own function, called \cpp|our_sys_open|.
1532+
Towards this end, we replace the system call to open a file with our own function, called \cpp|our_sys_openat|.
15331533
This function checks the uid (user's id) of the current process, and if it is equal to the uid we spy on, it calls \cpp|pr_info()| to display the name of the file to be opened.
1534-
Then, either way, it calls the original \cpp|open()| function with the same parameters, to actually open the file.
1534+
Then, either way, it calls the original \cpp|openat()| function with the same parameters, to actually open the file.
15351535

15361536
The \cpp|init_module| function replaces the appropriate location in \cpp|sys_call_table| and keeps the original pointer in a variable.
15371537
The \cpp|cleanup_module| function uses that variable to restore everything back to normal.
15381538
This approach is dangerous, because of the possibility of two kernel modules changing the same system call.
1539-
Imagine we have two kernel modules, A and B. A's open system call will be \cpp|A_open| and B's will be \cpp|B_open|.
1540-
Now, when A is inserted into the kernel, the system call is replaced with \cpp|A_open|, which will call the original \cpp|sys_open| when it is done.
1541-
Next, B is inserted into the kernel, which replaces the system call with \cpp|B_open|, which will call what it thinks is the original system call, \cpp|A_open|, when it's done.
1539+
Imagine we have two kernel modules, A and B. A's openat system call will be \cpp|A_openat| and B's will be \cpp|B_openat|.
1540+
Now, when A is inserted into the kernel, the system call is replaced with \cpp|A_openat|, which will call the original \cpp|sys_openat| when it is done.
1541+
Next, B is inserted into the kernel, which replaces the system call with \cpp|B_openat|, which will call what it thinks is the original system call, \cpp|A_openat|, when it's done.
15421542

1543-
Now, if B is removed first, everything will be well --- it will simply restore the system call to \cpp|A_open|, which calls the original.
1543+
Now, if B is removed first, everything will be well --- it will simply restore the system call to \cpp|A_openat|, which calls the original.
15441544
However, if A is removed and then B is removed, the system will crash.
1545-
A's removal will restore the system call to the original, \cpp|sys_open|, cutting B out of the loop.
1546-
Then, when B is removed, it will restore the system call to what it thinks is the original, \cpp|A_open|, which is no longer in memory.
1545+
A's removal will restore the system call to the original, \cpp|sys_openat|, cutting B out of the loop.
1546+
Then, when B is removed, it will restore the system call to what it thinks is the original, \cpp|A_openat|, which is no longer in memory.
15471547
At first glance, it appears we could solve this particular problem by checking if the system call is equal to our open function and if so not changing it at all (so that B won't change the system call when it is removed), but that will cause an even worse problem.
1548-
When A is removed, it sees that the system call was changed to \cpp|B_open| so that it is no longer pointing to \cpp|A_open|, so it will not restore it to \cpp|sys_open| before it is removed from memory.
1549-
Unfortunately, \cpp|B_open| will still try to call \cpp|A_open| which is no longer there, so that even without removing B the system would crash.
1548+
When A is removed, it sees that the system call was changed to \cpp|B_openat| so that it is no longer pointing to \cpp|A_openat|, so it will not restore it to \cpp|sys_openat| before it is removed from memory.
1549+
Unfortunately, \cpp|B_openat| will still try to call \cpp|A_openat| which is no longer there, so that even without removing B the system would crash.
15501550

15511551
Note that all the related problems make syscall stealing unfeasible for production use.
15521552
In order to keep people from doing potential harmful things \cpp|sys_call_table| is no longer exported.

0 commit comments

Comments
 (0)