The packet corruption code only flipped bits in the linear header portion of the skb, skipping corruption when skb_headlen() was zero. Use skb_header_pointer() and skb_store_bits() to access the full packet data, allowing any bit in the packet to be corrupted regardless of how the skb is laid out. Replaces d64cb81dcbd5 ("net/sched: sch_netem: fix out-of-bounds access in packet corruption") with a more general solution. Only count the number of packets that were actually corrupted. Signed-off-by: Stephen Hemminger --- net/sched/sch_netem.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index e710898ce96e..5cbd1a0dbfda 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -509,7 +509,6 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, * do it now in software before we mangle it. */ if (q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor, &q->prng)) { - WRITE_ONCE(q->corrupted, q->corrupted + 1); if (skb_is_gso(skb)) { skb = netem_segment(skb, sch, to_free); if (!skb) @@ -532,9 +531,18 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, goto finish_segs; } - if (skb_headlen(skb)) - skb->data[get_random_u32_below(skb_headlen(skb))] ^= - 1 << get_random_u32_below(8); + if (skb->len > 0) { + unsigned int offset = get_random_u32_below(skb->len); + u8 *ptr, val; + + /* handle multi-segment skb's */ + ptr = skb_header_pointer(skb, offset, 1, &val); + if (ptr) { + val = *ptr ^ (1 << get_random_u32_below(8)); + skb_store_bits(skb, offset, &val, 1); + WRITE_ONCE(q->corrupted, q->corrupted + 1); + } + } } if (unlikely(sch->q.qlen >= sch->limit)) { -- 2.53.0