Skip to content

Commit 62dbb1b

Browse files
authored
Adapt the timer API Usage (#98)
Since v4.14 [1], the timer API has been changed to improve memory safety. The series of improvements ended up at v4.15 [2]. Reference: https://lwn.net/Articles/735887/ Close #97 [1] torvalds/linux@686fef9 [2] torvalds/linux@841b86f
1 parent d5c4342 commit 62dbb1b

File tree

2 files changed

+50
-12
lines changed

2 files changed

+50
-12
lines changed

examples/kbleds.c

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,16 @@
55
#include <linux/init.h>
66
#include <linux/kd.h> /* For KDSETLED */
77
#include <linux/module.h>
8-
#include <linux/tty.h> /* For fg_console, MAX_NR_CONSOLES */
9-
#include <linux/vt.h>
8+
#include <linux/tty.h> /* For tty_struct */
9+
#include <linux/vt.h> /* For MAX_NR_CONSOLES */
1010
#include <linux/vt_kern.h> /* for fg_console */
11-
1211
#include <linux/console_struct.h> /* For vc_cons */
1312

1413
MODULE_DESCRIPTION("Example module illustrating the use of Keyboard LEDs.");
1514

1615
static struct timer_list my_timer;
1716
static struct tty_driver *my_driver;
18-
static char kbledstatus = 0;
17+
static unsigned long kbledstatus = 0;
1918

2019
#define BLINK_DELAY HZ / 5
2120
#define ALL_LEDS_ON 0x07
@@ -32,18 +31,16 @@ static char kbledstatus = 0;
3231
* the LEDs reflect the actual keyboard status). To learn more on this,
3332
* please see file: drivers/tty/vt/keyboard.c, function setledstate().
3433
*/
35-
36-
static void my_timer_func(unsigned long ptr)
34+
static void my_timer_func(struct timer_list *unused)
3735
{
38-
unsigned long *pstatus = (unsigned long *)ptr;
3936
struct tty_struct *t = vc_cons[fg_console].d->port.tty;
4037

41-
if (*pstatus == ALL_LEDS_ON)
42-
*pstatus = RESTORE_LEDS;
38+
if (kbledstatus == ALL_LEDS_ON)
39+
kbledstatus = RESTORE_LEDS;
4340
else
44-
*pstatus = ALL_LEDS_ON;
41+
kbledstatus = ALL_LEDS_ON;
4542

46-
(my_driver->ops->ioctl)(t, KDSETLED, *pstatus);
43+
(my_driver->ops->ioctl)(t, KDSETLED, kbledstatus);
4744

4845
my_timer.expires = jiffies + BLINK_DELAY;
4946
add_timer(&my_timer);
@@ -67,7 +64,7 @@ static int __init kbleds_init(void)
6764
pr_info("kbleds: tty driver magic %x\n", my_driver->magic);
6865

6966
/* Set up the LED blink timer the first time. */
70-
timer_setup(&my_timer, (void *)&my_timer_func, (unsigned long)&kbledstatus);
67+
timer_setup(&my_timer, my_timer_func, 0);
7168
my_timer.expires = jiffies + BLINK_DELAY;
7269
add_timer(&my_timer);
7370

lkmpg.tex

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1502,6 +1502,47 @@ \subsection{Flashing keyboard LEDs}
15021502
Flashing keyboard LEDs can be such a solution: It is an immediate way to attract attention or to display a status condition.
15031503
Keyboard LEDs are present on every hardware, they are always visible, they do not need any setup, and their use is rather simple and non-intrusive, compared to writing to a tty or a file.
15041504

1505+
From v4.14 to v4.15, the timer API made a series of changes to improve memory safety.
1506+
A buffer overflow in the area of a \cpp|timer_list| structure may be able to overwrite the \cpp|function| and \cpp|data| fields, providing the attacker with a way to use return-object programming (ROP) to call arbitrary functions within the kernel.
1507+
Also, the function prototype of the callback, containing a \cpp|unsigned long| argument, will prevent work from any type checking.
1508+
Furthermore, the function prototype with \cpp|unsigned long| argument may be an obstacle to the \textit{control-flow integrity}.
1509+
Thus, it is better to use a unique prototype to separate from the cluster that takes an \cpp|unsigned long| argument.
1510+
The timer callback should be passed a pointer to the \cpp|timer_list| structure rather than an \cpp|unsigned long| argument.
1511+
Then, it wraps all the information the callback needs, including the \cpp|timer_list| structure, into a larger structure, and it can use the \cpp|container_of| macro instead of the \cpp|unsigned long| value.
1512+
1513+
Before Linux v4.14, \cpp|setup_timer| was used to initialize the timer and the \cpp|timer_list| structure looked like:
1514+
\begin{code}
1515+
struct timer_list {
1516+
unsigned long expires;
1517+
void (*function)(unsigned long);
1518+
unsigned long data;
1519+
u32 flags;
1520+
/* ... */
1521+
};
1522+
1523+
void setup_timer(struct timer_list *timer, void (*callback)(unsigned long),
1524+
unsigned long data);
1525+
\end{code}
1526+
1527+
Since Linux v4.14, \cpp|timer_setup| is adopted and the kernel step by step converting to \cpp|timer_setup| from \cpp|setup_timer|.
1528+
One of the reasons why API was changed is it need to coexist with the old version interface.
1529+
Moreover, the \cpp|timer_setup| was implemented by \cpp|setup_timer| at first.
1530+
\begin{code}
1531+
void timer_setup(struct timer_list *timer,
1532+
void (*callback)(struct timer_list *), unsigned int flags);
1533+
\end{code}
1534+
1535+
The \cpp|setup_timer| was then removed since v4.15.
1536+
As a result, the \cpp|timer_list| structure had changed to the following.
1537+
\begin{code}
1538+
struct timer_list {
1539+
unsigned long expires;
1540+
void (*function)(struct timer_list *);
1541+
u32 flags;
1542+
/* ... */
1543+
};
1544+
\end{code}
1545+
15051546
The following source code illustrates a minimal kernel module which, when loaded, starts blinking the keyboard LEDs until it is unloaded.
15061547

15071548
\samplec{examples/kbleds.c}

0 commit comments

Comments
 (0)