Skip to content

Commit 4f59652

Browse files
Florian Westphalkelmously
authored andcommitted
netfilter: ebtables: compat: reject all padding in matches/watchers
BugLink: https://bugs.launchpad.net/bugs/1860816 commit e608f63 upstream. syzbot reported following splat: BUG: KASAN: vmalloc-out-of-bounds in size_entry_mwt net/bridge/netfilter/ebtables.c:2063 [inline] BUG: KASAN: vmalloc-out-of-bounds in compat_copy_entries+0x128b/0x1380 net/bridge/netfilter/ebtables.c:2155 Read of size 4 at addr ffffc900004461f4 by task syz-executor267/7937 CPU: 1 PID: 7937 Comm: syz-executor267 Not tainted 5.5.0-rc1-syzkaller #0 size_entry_mwt net/bridge/netfilter/ebtables.c:2063 [inline] compat_copy_entries+0x128b/0x1380 net/bridge/netfilter/ebtables.c:2155 compat_do_replace+0x344/0x720 net/bridge/netfilter/ebtables.c:2249 compat_do_ebt_set_ctl+0x22f/0x27e net/bridge/netfilter/ebtables.c:2333 [..] Because padding isn't considered during computation of ->buf_user_offset, "total" is decremented by fewer bytes than it should. Therefore, the first part of if (*total < sizeof(*entry) || entry->next_offset < sizeof(*entry)) will pass, -- it should not have. This causes oob access: entry->next_offset is past the vmalloced size. Reject padding and check that computed user offset (sum of ebt_entry structure plus all individual matches/watchers/targets) is same value that userspace gave us as the offset of the next entry. Reported-by: [email protected] Fixes: 81e675c ("netfilter: ebtables: add CONFIG_COMPAT support") Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]> Signed-off-by: Kamal Mostafa <[email protected]> Signed-off-by: Khalid Elmously <[email protected]>
1 parent 91f0673 commit 4f59652

File tree

1 file changed

+16
-17
lines changed

1 file changed

+16
-17
lines changed

net/bridge/netfilter/ebtables.c

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1867,7 +1867,7 @@ static int ebt_buf_count(struct ebt_entries_buf_state *state, unsigned int sz)
18671867
}
18681868

18691869
static int ebt_buf_add(struct ebt_entries_buf_state *state,
1870-
void *data, unsigned int sz)
1870+
const void *data, unsigned int sz)
18711871
{
18721872
if (state->buf_kern_start == NULL)
18731873
goto count_only;
@@ -1901,7 +1901,7 @@ enum compat_mwt {
19011901
EBT_COMPAT_TARGET,
19021902
};
19031903

1904-
static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt,
1904+
static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt,
19051905
enum compat_mwt compat_mwt,
19061906
struct ebt_entries_buf_state *state,
19071907
const unsigned char *base)
@@ -1979,22 +1979,23 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt,
19791979
/* return size of all matches, watchers or target, including necessary
19801980
* alignment and padding.
19811981
*/
1982-
static int ebt_size_mwt(struct compat_ebt_entry_mwt *match32,
1982+
static int ebt_size_mwt(const struct compat_ebt_entry_mwt *match32,
19831983
unsigned int size_left, enum compat_mwt type,
19841984
struct ebt_entries_buf_state *state, const void *base)
19851985
{
1986+
const char *buf = (const char *)match32;
19861987
int growth = 0;
1987-
char *buf;
19881988

19891989
if (size_left == 0)
19901990
return 0;
19911991

1992-
buf = (char *) match32;
1993-
1994-
while (size_left >= sizeof(*match32)) {
1992+
do {
19951993
struct ebt_entry_match *match_kern;
19961994
int ret;
19971995

1996+
if (size_left < sizeof(*match32))
1997+
return -EINVAL;
1998+
19981999
match_kern = (struct ebt_entry_match *) state->buf_kern_start;
19992000
if (match_kern) {
20002001
char *tmp;
@@ -2031,22 +2032,18 @@ static int ebt_size_mwt(struct compat_ebt_entry_mwt *match32,
20312032
if (match_kern)
20322033
match_kern->match_size = ret;
20332034

2034-
/* rule should have no remaining data after target */
2035-
if (type == EBT_COMPAT_TARGET && size_left)
2036-
return -EINVAL;
2037-
20382035
match32 = (struct compat_ebt_entry_mwt *) buf;
2039-
}
2036+
} while (size_left);
20402037

20412038
return growth;
20422039
}
20432040

20442041
/* called for all ebt_entry structures. */
2045-
static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base,
2042+
static int size_entry_mwt(const struct ebt_entry *entry, const unsigned char *base,
20462043
unsigned int *total,
20472044
struct ebt_entries_buf_state *state)
20482045
{
2049-
unsigned int i, j, startoff, new_offset = 0;
2046+
unsigned int i, j, startoff, next_expected_off, new_offset = 0;
20502047
/* stores match/watchers/targets & offset of next struct ebt_entry: */
20512048
unsigned int offsets[4];
20522049
unsigned int *offsets_update = NULL;
@@ -2132,11 +2129,13 @@ static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base,
21322129
return ret;
21332130
}
21342131

2135-
startoff = state->buf_user_offset - startoff;
2132+
next_expected_off = state->buf_user_offset - startoff;
2133+
if (next_expected_off != entry->next_offset)
2134+
return -EINVAL;
21362135

2137-
if (WARN_ON(*total < startoff))
2136+
if (*total < entry->next_offset)
21382137
return -EINVAL;
2139-
*total -= startoff;
2138+
*total -= entry->next_offset;
21402139
return 0;
21412140
}
21422141

0 commit comments

Comments
 (0)