Skip to content

Commit b7213ff

Browse files
committed
qnx4: avoid stringop-overread errors
The qnx4 directory entries are 64-byte blocks that have different contents depending on the a status byte that is in the last byte of the block. In particular, a directory entry can be either a "link info" entry with a 48-byte name and pointers to the real inode information, or an "inode entry" with a smaller 16-byte name and the full inode information. But the code was written to always just treat the directory name as if it was part of that "inode entry", and just extend the name to the longer case if the status byte said it was a link entry. That work just fine and gives the right results, but now that gcc is tracking data structure accesses much more, the code can trigger a compiler error about using up to 48 bytes (the long name) in a structure that only has that shorter name in it: fs/qnx4/dir.c: In function ‘qnx4_readdir’: fs/qnx4/dir.c:51:32: error: ‘strnlen’ specified bound 48 exceeds source size 16 [-Werror=stringop-overread] 51 | size = strnlen(de->di_fname, size); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from fs/qnx4/qnx4.h:3, from fs/qnx4/dir.c:16: include/uapi/linux/qnx4_fs.h:45:25: note: source object declared here 45 | char di_fname[QNX4_SHORT_NAME_MAX]; | ^~~~~~~~ which is because the source code doesn't really make this whole "one of two different types" explicit. Fix this by introducing a very explicit union of the two types, and basically explaining to the compiler what is really going on. Signed-off-by: Linus Torvalds <[email protected]>
1 parent fc7c028 commit b7213ff

File tree

1 file changed

+34
-17
lines changed

1 file changed

+34
-17
lines changed

fs/qnx4/dir.c

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,27 @@
1515
#include <linux/buffer_head.h>
1616
#include "qnx4.h"
1717

18+
/*
19+
* A qnx4 directory entry is an inode entry or link info
20+
* depending on the status field in the last byte. The
21+
* first byte is where the name start either way, and a
22+
* zero means it's empty.
23+
*/
24+
union qnx4_directory_entry {
25+
struct {
26+
char de_name;
27+
char de_pad[62];
28+
char de_status;
29+
};
30+
struct qnx4_inode_entry inode;
31+
struct qnx4_link_info link;
32+
};
33+
1834
static int qnx4_readdir(struct file *file, struct dir_context *ctx)
1935
{
2036
struct inode *inode = file_inode(file);
2137
unsigned int offset;
2238
struct buffer_head *bh;
23-
struct qnx4_inode_entry *de;
24-
struct qnx4_link_info *le;
2539
unsigned long blknum;
2640
int ix, ino;
2741
int size;
@@ -38,27 +52,30 @@ static int qnx4_readdir(struct file *file, struct dir_context *ctx)
3852
}
3953
ix = (ctx->pos >> QNX4_DIR_ENTRY_SIZE_BITS) % QNX4_INODES_PER_BLOCK;
4054
for (; ix < QNX4_INODES_PER_BLOCK; ix++, ctx->pos += QNX4_DIR_ENTRY_SIZE) {
55+
union qnx4_directory_entry *de;
56+
const char *name;
57+
4158
offset = ix * QNX4_DIR_ENTRY_SIZE;
42-
de = (struct qnx4_inode_entry *) (bh->b_data + offset);
43-
if (!de->di_fname[0])
59+
de = (union qnx4_directory_entry *) (bh->b_data + offset);
60+
61+
if (!de->de_name)
4462
continue;
45-
if (!(de->di_status & (QNX4_FILE_USED|QNX4_FILE_LINK)))
63+
if (!(de->de_status & (QNX4_FILE_USED|QNX4_FILE_LINK)))
4664
continue;
47-
if (!(de->di_status & QNX4_FILE_LINK))
48-
size = QNX4_SHORT_NAME_MAX;
49-
else
50-
size = QNX4_NAME_MAX;
51-
size = strnlen(de->di_fname, size);
52-
QNX4DEBUG((KERN_INFO "qnx4_readdir:%.*s\n", size, de->di_fname));
53-
if (!(de->di_status & QNX4_FILE_LINK))
65+
if (!(de->de_status & QNX4_FILE_LINK)) {
66+
size = sizeof(de->inode.di_fname);
67+
name = de->inode.di_fname;
5468
ino = blknum * QNX4_INODES_PER_BLOCK + ix - 1;
55-
else {
56-
le = (struct qnx4_link_info*)de;
57-
ino = ( le32_to_cpu(le->dl_inode_blk) - 1 ) *
69+
} else {
70+
size = sizeof(de->link.dl_fname);
71+
name = de->link.dl_fname;
72+
ino = ( le32_to_cpu(de->link.dl_inode_blk) - 1 ) *
5873
QNX4_INODES_PER_BLOCK +
59-
le->dl_inode_ndx;
74+
de->link.dl_inode_ndx;
6075
}
61-
if (!dir_emit(ctx, de->di_fname, size, ino, DT_UNKNOWN)) {
76+
size = strnlen(name, size);
77+
QNX4DEBUG((KERN_INFO "qnx4_readdir:%.*s\n", size, name));
78+
if (!dir_emit(ctx, name, size, ino, DT_UNKNOWN)) {
6279
brelse(bh);
6380
return 0;
6481
}

0 commit comments

Comments
 (0)