为什么在 Linux hwmon 中允许可能存在缓冲区溢出的 sprintf?

Why is sprintf with possible buffer overflow allowed in Linux hwmon?

我已经看到以下代码片段被重复用于 Linux hwmon 设备:

return sprintf(buf, "%d\n", in_input);

其中 buf 是指向 char char *buf 的指针,而 in_input 通常是 intu16。目的是将从设备读回的值复制到为此设备属性创建的sysfs文件。

作为示例,您可以查看 Linux/drivers/hwmon/mcp3021.c(或 4.9 内核之前的任何 hwmon 设备)。您可以在第 81 行 returns a u16 看到该函数,在第 99 行代码将 u16 存储到 char *buf.

 81 static inline u16 volts_from_reg(struct mcp3021_data *data, u16 val)
 82 {
 83         return DIV_ROUND_CLOSEST(data->vdd * val, 1 << data->output_res);
 84 }
 85 
 86 static ssize_t show_in_input(struct device *dev, struct device_attribute *attr,
 87                 char *buf)
 88 {
 89         struct i2c_client *client = to_i2c_client(dev);
 90         struct mcp3021_data *data = i2c_get_clientdata(client);
 91         int reg, in_input;
 92 
 93         reg = mcp3021_read16(client);
 94         if (reg < 0)
 95                 return reg;
 96 
 97         in_input = volts_from_reg(data, reg);
 98 
 99         return sprintf(buf, "%d\n", in_input);
100 }

这段代码不会总是导致缓冲区溢出吗?我们总是将 u16 存储到分配给 char 8 位的缓冲区中。为什么 Linux 设备驱动程序允许这样做?

请注意,对于执行完全相同操作的我的驱动程序,sysfs 会正确显示返回值,即使它不可能存储为 char(8 位)。所以想知道 char * 表示是否不准确?

发布的代码显示糟糕的编程风格,但如果已知 buf 指向至少 8 字节的数组,则行为已定义并且 sprintf 不会导致缓冲区溢出。

请注意,show_in_input 不接收目标缓冲区的大小,需要更改 API 才能使用 snprintf()

根据 sysfs.txt 中的文档,传递给 show 函数的缓冲区大小为 PAGE_SIZE:

sysfs allocates a buffer of size (PAGE_SIZE) and passes it to the method. Sysfs will call the method exactly once for each read or write.

由于PAGE_SIZE肯定比一个小整数的长度大,缓冲区溢出的实际可能性不大。