#include <linux/init.h>
#include <linux/kd.h> /* For KDSETLED */
#include <linux/module.h>
-#include <linux/tty.h> /* For fg_console, MAX_NR_CONSOLES */
-#include <linux/vt.h>
+#include <linux/tty.h> /* For tty_struct */
+#include <linux/vt.h> /* For MAX_NR_CONSOLES */
#include <linux/vt_kern.h> /* for fg_console */
-
#include <linux/console_struct.h> /* For vc_cons */
MODULE_DESCRIPTION("Example module illustrating the use of Keyboard LEDs.");
static struct timer_list my_timer;
static struct tty_driver *my_driver;
-static char kbledstatus = 0;
+static unsigned long kbledstatus = 0;
#define BLINK_DELAY HZ / 5
#define ALL_LEDS_ON 0x07
* the LEDs reflect the actual keyboard status). To learn more on this,
* please see file: drivers/tty/vt/keyboard.c, function setledstate().
*/
-
-static void my_timer_func(unsigned long ptr)
+static void my_timer_func(struct timer_list *unused)
{
- unsigned long *pstatus = (unsigned long *)ptr;
struct tty_struct *t = vc_cons[fg_console].d->port.tty;
- if (*pstatus == ALL_LEDS_ON)
- *pstatus = RESTORE_LEDS;
+ if (kbledstatus == ALL_LEDS_ON)
+ kbledstatus = RESTORE_LEDS;
else
- *pstatus = ALL_LEDS_ON;
+ kbledstatus = ALL_LEDS_ON;
- (my_driver->ops->ioctl)(t, KDSETLED, *pstatus);
+ (my_driver->ops->ioctl)(t, KDSETLED, kbledstatus);
my_timer.expires = jiffies + BLINK_DELAY;
add_timer(&my_timer);
pr_info("kbleds: tty driver magic %x\n", my_driver->magic);
/* Set up the LED blink timer the first time. */
- timer_setup(&my_timer, (void *)&my_timer_func, (unsigned long)&kbledstatus);
+ timer_setup(&my_timer, my_timer_func, 0);
my_timer.expires = jiffies + BLINK_DELAY;
add_timer(&my_timer);
Flashing keyboard LEDs can be such a solution: It is an immediate way to attract attention or to display a status condition.
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.
+From v4.14 to v4.15, the timer API made a series of changes to improve memory safety.
+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.
+Also, the function prototype of the callback, containing a \cpp|unsigned long| argument, will prevent work from any type checking.
+Furthermore, the function prototype with \cpp|unsigned long| argument may be an obstacle to the \textit{control-flow integrity}.
+Thus, it is better to use a unique prototype to separate from the cluster that takes an \cpp|unsigned long| argument.
+The timer callback should be passed a pointer to the \cpp|timer_list| structure rather than an \cpp|unsigned long| argument.
+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.
+
+Before Linux v4.14, \cpp|setup_timer| was used to initialize the timer and the \cpp|timer_list| structure looked like:
+\begin{code}
+struct timer_list {
+ unsigned long expires;
+ void (*function)(unsigned long);
+ unsigned long data;
+ u32 flags;
+ /* ... */
+};
+
+void setup_timer(struct timer_list *timer, void (*callback)(unsigned long),
+ unsigned long data);
+\end{code}
+
+Since Linux v4.14, \cpp|timer_setup| is adopted and the kernel step by step converting to \cpp|timer_setup| from \cpp|setup_timer|.
+One of the reasons why API was changed is it need to coexist with the old version interface.
+Moreover, the \cpp|timer_setup| was implemented by \cpp|setup_timer| at first.
+\begin{code}
+void timer_setup(struct timer_list *timer,
+ void (*callback)(struct timer_list *), unsigned int flags);
+\end{code}
+
+The \cpp|setup_timer| was then removed since v4.15.
+As a result, the \cpp|timer_list| structure had changed to the following.
+\begin{code}
+struct timer_list {
+ unsigned long expires;
+ void (*function)(struct timer_list *);
+ u32 flags;
+ /* ... */
+};
+\end{code}
+
The following source code illustrates a minimal kernel module which, when loaded, starts blinking the keyboard LEDs until it is unloaded.
\samplec{examples/kbleds.c}