Skip to content

Commit 0fa8e08

Browse files
keesgregkh
authored andcommitted
fs/kernel_file_read: Add "offset" arg for partial reads
To perform partial reads, callers of kernel_read_file*() must have a non-NULL file_size argument and a preallocated buffer. The new "offset" argument can then be used to seek to specific locations in the file to fill the buffer to, at most, "buf_size" per call. Where possible, the LSM hooks can report whether a full file has been read or not so that the contents can be reasoned about. Signed-off-by: Kees Cook <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 34736da commit 0fa8e08

File tree

7 files changed

+65
-34
lines changed

7 files changed

+65
-34
lines changed

drivers/base/firmware_loader/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
499499
fw_priv->size = 0;
500500

501501
/* load firmware files from the mount namespace of init */
502-
rc = kernel_read_file_from_path_initns(path, &buffer, msize,
502+
rc = kernel_read_file_from_path_initns(path, 0, &buffer, msize,
503503
NULL,
504504
READING_FIRMWARE);
505505
if (rc < 0) {

fs/kernel_read_file.c

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* kernel_read_file() - read file contents into a kernel buffer
1010
*
1111
* @file file to read from
12+
* @offset where to start reading from (see below).
1213
* @buf pointer to a "void *" buffer for reading into (if
1314
* *@buf is NULL, a buffer will be allocated, and
1415
* @buf_size will be ignored)
@@ -19,39 +20,59 @@
1920
* @id the kernel_read_file_id identifying the type of
2021
* file contents being read (for LSMs to examine)
2122
*
23+
* @offset must be 0 unless both @buf and @file_size are non-NULL
24+
* (i.e. the caller must be expecting to read partial file contents
25+
* via an already-allocated @buf, in at most @buf_size chunks, and
26+
* will be able to determine when the entire file was read by
27+
* checking @file_size). This isn't a recommended way to read a
28+
* file, though, since it is possible that the contents might
29+
* change between calls to kernel_read_file().
30+
*
2231
* Returns number of bytes read (no single read will be bigger
2332
* than INT_MAX), or negative on error.
2433
*
2534
*/
26-
int kernel_read_file(struct file *file, void **buf,
35+
int kernel_read_file(struct file *file, loff_t offset, void **buf,
2736
size_t buf_size, size_t *file_size,
2837
enum kernel_read_file_id id)
2938
{
3039
loff_t i_size, pos;
31-
ssize_t bytes = 0;
40+
size_t copied;
3241
void *allocated = NULL;
42+
bool whole_file;
3343
int ret;
3444

45+
if (offset != 0 && (!*buf || !file_size))
46+
return -EINVAL;
47+
3548
if (!S_ISREG(file_inode(file)->i_mode))
3649
return -EINVAL;
3750

3851
ret = deny_write_access(file);
3952
if (ret)
4053
return ret;
4154

42-
ret = security_kernel_read_file(file, id, true);
43-
if (ret)
44-
goto out;
45-
4655
i_size = i_size_read(file_inode(file));
4756
if (i_size <= 0) {
4857
ret = -EINVAL;
4958
goto out;
5059
}
51-
if (i_size > INT_MAX || i_size > buf_size) {
60+
/* The file is too big for sane activities. */
61+
if (i_size > INT_MAX) {
62+
ret = -EFBIG;
63+
goto out;
64+
}
65+
/* The entire file cannot be read in one buffer. */
66+
if (!file_size && offset == 0 && i_size > buf_size) {
5267
ret = -EFBIG;
5368
goto out;
5469
}
70+
71+
whole_file = (offset == 0 && i_size <= buf_size);
72+
ret = security_kernel_read_file(file, id, whole_file);
73+
if (ret)
74+
goto out;
75+
5576
if (file_size)
5677
*file_size = i_size;
5778

@@ -62,24 +83,32 @@ int kernel_read_file(struct file *file, void **buf,
6283
goto out;
6384
}
6485

65-
pos = 0;
66-
while (pos < i_size) {
67-
bytes = kernel_read(file, *buf + pos, i_size - pos, &pos);
86+
pos = offset;
87+
copied = 0;
88+
while (copied < buf_size) {
89+
ssize_t bytes;
90+
size_t wanted = min_t(size_t, buf_size - copied,
91+
i_size - pos);
92+
93+
bytes = kernel_read(file, *buf + copied, wanted, &pos);
6894
if (bytes < 0) {
6995
ret = bytes;
7096
goto out_free;
7197
}
7298

7399
if (bytes == 0)
74100
break;
101+
copied += bytes;
75102
}
76103

77-
if (pos != i_size) {
78-
ret = -EIO;
79-
goto out_free;
80-
}
104+
if (whole_file) {
105+
if (pos != i_size) {
106+
ret = -EIO;
107+
goto out_free;
108+
}
81109

82-
ret = security_kernel_post_read_file(file, *buf, i_size, id);
110+
ret = security_kernel_post_read_file(file, *buf, i_size, id);
111+
}
83112

84113
out_free:
85114
if (ret < 0) {
@@ -91,11 +120,11 @@ int kernel_read_file(struct file *file, void **buf,
91120

92121
out:
93122
allow_write_access(file);
94-
return ret == 0 ? pos : ret;
123+
return ret == 0 ? copied : ret;
95124
}
96125
EXPORT_SYMBOL_GPL(kernel_read_file);
97126

98-
int kernel_read_file_from_path(const char *path, void **buf,
127+
int kernel_read_file_from_path(const char *path, loff_t offset, void **buf,
99128
size_t buf_size, size_t *file_size,
100129
enum kernel_read_file_id id)
101130
{
@@ -109,14 +138,15 @@ int kernel_read_file_from_path(const char *path, void **buf,
109138
if (IS_ERR(file))
110139
return PTR_ERR(file);
111140

112-
ret = kernel_read_file(file, buf, buf_size, file_size, id);
141+
ret = kernel_read_file(file, offset, buf, buf_size, file_size, id);
113142
fput(file);
114143
return ret;
115144
}
116145
EXPORT_SYMBOL_GPL(kernel_read_file_from_path);
117146

118-
int kernel_read_file_from_path_initns(const char *path, void **buf,
119-
size_t buf_size, size_t *file_size,
147+
int kernel_read_file_from_path_initns(const char *path, loff_t offset,
148+
void **buf, size_t buf_size,
149+
size_t *file_size,
120150
enum kernel_read_file_id id)
121151
{
122152
struct file *file;
@@ -135,14 +165,14 @@ int kernel_read_file_from_path_initns(const char *path, void **buf,
135165
if (IS_ERR(file))
136166
return PTR_ERR(file);
137167

138-
ret = kernel_read_file(file, buf, buf_size, file_size, id);
168+
ret = kernel_read_file(file, offset, buf, buf_size, file_size, id);
139169
fput(file);
140170
return ret;
141171
}
142172
EXPORT_SYMBOL_GPL(kernel_read_file_from_path_initns);
143173

144-
int kernel_read_file_from_fd(int fd, void **buf, size_t buf_size,
145-
size_t *file_size,
174+
int kernel_read_file_from_fd(int fd, loff_t offset, void **buf,
175+
size_t buf_size, size_t *file_size,
146176
enum kernel_read_file_id id)
147177
{
148178
struct fd f = fdget(fd);
@@ -151,7 +181,7 @@ int kernel_read_file_from_fd(int fd, void **buf, size_t buf_size,
151181
if (!f.file)
152182
goto out;
153183

154-
ret = kernel_read_file(f.file, buf, buf_size, file_size, id);
184+
ret = kernel_read_file(f.file, offset, buf, buf_size, file_size, id);
155185
out:
156186
fdput(f);
157187
return ret;

include/linux/kernel_read_file.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,19 @@ static inline const char *kernel_read_file_id_str(enum kernel_read_file_id id)
3535
return kernel_read_file_str[id];
3636
}
3737

38-
int kernel_read_file(struct file *file,
38+
int kernel_read_file(struct file *file, loff_t offset,
3939
void **buf, size_t buf_size,
4040
size_t *file_size,
4141
enum kernel_read_file_id id);
42-
int kernel_read_file_from_path(const char *path,
42+
int kernel_read_file_from_path(const char *path, loff_t offset,
4343
void **buf, size_t buf_size,
4444
size_t *file_size,
4545
enum kernel_read_file_id id);
46-
int kernel_read_file_from_path_initns(const char *path,
46+
int kernel_read_file_from_path_initns(const char *path, loff_t offset,
4747
void **buf, size_t buf_size,
4848
size_t *file_size,
4949
enum kernel_read_file_id id);
50-
int kernel_read_file_from_fd(int fd,
50+
int kernel_read_file_from_fd(int fd, loff_t offset,
5151
void **buf, size_t buf_size,
5252
size_t *file_size,
5353
enum kernel_read_file_id id);

kernel/kexec_file.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
221221
int ret;
222222
void *ldata;
223223

224-
ret = kernel_read_file_from_fd(kernel_fd, &image->kernel_buf,
224+
ret = kernel_read_file_from_fd(kernel_fd, 0, &image->kernel_buf,
225225
INT_MAX, NULL, READING_KEXEC_IMAGE);
226226
if (ret < 0)
227227
return ret;
@@ -241,7 +241,7 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
241241
#endif
242242
/* It is possible that there no initramfs is being loaded */
243243
if (!(flags & KEXEC_FILE_NO_INITRAMFS)) {
244-
ret = kernel_read_file_from_fd(initrd_fd, &image->initrd_buf,
244+
ret = kernel_read_file_from_fd(initrd_fd, 0, &image->initrd_buf,
245245
INT_MAX, NULL,
246246
READING_KEXEC_INITRAMFS);
247247
if (ret < 0)

kernel/module.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4054,7 +4054,7 @@ SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags)
40544054
|MODULE_INIT_IGNORE_VERMAGIC))
40554055
return -EINVAL;
40564056

4057-
err = kernel_read_file_from_fd(fd, &hdr, INT_MAX, NULL,
4057+
err = kernel_read_file_from_fd(fd, 0, &hdr, INT_MAX, NULL,
40584058
READING_MODULE);
40594059
if (err < 0)
40604060
return err;

security/integrity/digsig.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ int __init integrity_load_x509(const unsigned int id, const char *path)
175175
int rc;
176176
key_perm_t perm;
177177

178-
rc = kernel_read_file_from_path(path, &data, INT_MAX, NULL,
178+
rc = kernel_read_file_from_path(path, 0, &data, INT_MAX, NULL,
179179
READING_X509_CERTIFICATE);
180180
if (rc < 0) {
181181
pr_err("Unable to open file: %s (%d)", path, rc);

security/integrity/ima/ima_fs.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,8 @@ static ssize_t ima_read_policy(char *path)
284284
datap = path;
285285
strsep(&datap, "\n");
286286

287-
rc = kernel_read_file_from_path(path, &data, INT_MAX, NULL, READING_POLICY);
287+
rc = kernel_read_file_from_path(path, 0, &data, INT_MAX, NULL,
288+
READING_POLICY);
288289
if (rc < 0) {
289290
pr_err("Unable to open file: %s (%d)", path, rc);
290291
return rc;

0 commit comments

Comments
 (0)