blob: e086843c42b68852d2a15757f0ff27e253052e3c [file] [log] [blame]
/* Adapted from linux 5.3.11: drivers/net/wireless/ath/ath10k/usb.c
Reduced reproducer for CVE-2019-19078 (leak of struct urb). */
typedef unsigned char u8;
typedef unsigned short u16;
typedef _Bool bool;
#define ENOMEM 12
#define EINVAL 22
/* The original file has this licence header. */
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2007-2011 Atheros Communications Inc.
* Copyright (c) 2011-2012,2017 Qualcomm Atheros, Inc.
* Copyright (c) 2016-2017 Erik Stromdahl <erik.stromdahl@gmail.com>
*/
/* Adapted from include/linux/compiler_attributes.h. */
#define __aligned(x) __attribute__((__aligned__(x)))
#define __printf(a, b) __attribute__((__format__(printf, a, b)))
/* Possible macro for the new attribute. */
#define __malloc(f) __attribute__((malloc(f)));
/* From include/linux/types.h. */
typedef unsigned int gfp_t;
/* Not the real value, which is in include/linux/gfp.h. */
#define GFP_ATOMIC 32
/* From include/linux/usb.h. */
struct urb;
extern void usb_free_urb(struct urb *urb);
extern struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
__malloc(usb_free_urb);
/* attribute added as part of testcase */
extern int usb_submit_urb(/*struct urb *urb, */gfp_t mem_flags);
extern void usb_unanchor_urb(struct urb *urb);
/* From drivers/net/wireless/ath/ath10k/core.h. */
struct ath10k;
struct ath10k {
/* [...many other fields removed...] */
/* must be last */
u8 drv_priv[0] __aligned(sizeof(void *));
};
/* From drivers/net/wireless/ath/ath10k/debug.h. */
enum ath10k_debug_mask {
/* [...other values removed...] */
ATH10K_DBG_USB_BULK = 0x00080000,
};
extern unsigned int ath10k_debug_mask;
__printf(3, 4) void __ath10k_dbg(struct ath10k *ar,
enum ath10k_debug_mask mask,
const char *fmt, ...);
/* Simplified for now, to avoid pulling in tracepoint code. */
static inline
bool trace_ath10k_log_dbg_enabled(void) { return 0; }
#define ath10k_dbg(ar, dbg_mask, fmt, ...) \
do { \
if ((ath10k_debug_mask & dbg_mask) || \
trace_ath10k_log_dbg_enabled()) \
__ath10k_dbg(ar, dbg_mask, fmt, ##__VA_ARGS__); \
} while (0)
/* From drivers/net/wireless/ath/ath10k/hif.h. */
struct ath10k_hif_sg_item {
/* [...other fields removed...] */
void *transfer_context; /* NULL = tx completion callback not called */
};
struct ath10k_hif_ops {
/* send a scatter-gather list to the target */
int (*tx_sg)(struct ath10k *ar, u8 pipe_id,
struct ath10k_hif_sg_item *items, int n_items);
/* [...other fields removed...] */
};
/* From drivers/net/wireless/ath/ath10k/usb.h. */
/* tx/rx pipes for usb */
enum ath10k_usb_pipe_id {
/* [...other values removed...] */
ATH10K_USB_PIPE_MAX = 8
};
struct ath10k_usb_pipe {
/* [...all fields removed...] */
};
/* usb device object */
struct ath10k_usb {
/* [...other fields removed...] */
struct ath10k_usb_pipe pipes[ATH10K_USB_PIPE_MAX];
};
/* usb urb object */
struct ath10k_urb_context {
/* [...other fields removed...] */
struct ath10k_usb_pipe *pipe;
struct sk_buff *skb;
};
static inline struct ath10k_usb *ath10k_usb_priv(struct ath10k *ar)
{
return (struct ath10k_usb *)ar->drv_priv;
}
/* The source file. */
static void ath10k_usb_post_recv_transfers(struct ath10k *ar,
struct ath10k_usb_pipe *recv_pipe);
struct ath10k_urb_context *
ath10k_usb_alloc_urb_from_pipe(struct ath10k_usb_pipe *pipe);
void ath10k_usb_free_urb_to_pipe(struct ath10k_usb_pipe *pipe,
struct ath10k_urb_context *urb_context);
static int ath10k_usb_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
struct ath10k_hif_sg_item *items, int n_items)
{
struct ath10k_usb *ar_usb = ath10k_usb_priv(ar);
struct ath10k_usb_pipe *pipe = &ar_usb->pipes[pipe_id];
struct ath10k_urb_context *urb_context;
struct sk_buff *skb;
struct urb *urb;
int ret, i;
for (i = 0; i < n_items; i++) {
urb_context = ath10k_usb_alloc_urb_from_pipe(pipe);
if (!urb_context) {
ret = -ENOMEM;
goto err;
}
skb = items[i].transfer_context;
urb_context->skb = skb;
urb = usb_alloc_urb(0, GFP_ATOMIC); /* { dg-message "allocated here" } */
if (!urb) {
ret = -ENOMEM;
goto err_free_urb_to_pipe;
}
/* TODO: these are disabled, otherwise we conservatively
assume that they could free urb. */
#if 0
usb_fill_bulk_urb(urb,
ar_usb->udev,
pipe->usb_pipe_handle,
skb->data,
skb->len,
ath10k_usb_transmit_complete, urb_context);
if (!(skb->len % pipe->max_packet_size)) {
/* hit a max packet boundary on this pipe */
urb->transfer_flags |= URB_ZERO_PACKET;
}
usb_anchor_urb(urb, &pipe->urb_submitted);
#endif
/* TODO: initial argument disabled, otherwise we conservatively
assume that it could free urb. */
ret = usb_submit_urb(/*urb, */GFP_ATOMIC);
if (ret) { /* TODO: why doesn't it show this condition at default verbosity? */
ath10k_dbg(ar, ATH10K_DBG_USB_BULK,
"usb bulk transmit failed: %d\n", ret);
/* TODO: this is disabled, otherwise we conservatively
assume that it could free urb. */
#if 0
usb_unanchor_urb(urb);
#endif
ret = -EINVAL;
/* Leak of urb happens here. */
goto err_free_urb_to_pipe;
}
usb_free_urb(urb); /* { dg-bogus "double-'usb_free_urb' of 'urb'" } */
}
return 0;
err_free_urb_to_pipe:
ath10k_usb_free_urb_to_pipe(urb_context->pipe, urb_context);
err:
return ret; /* { dg-warning "leak of 'urb'" } */
}
static const struct ath10k_hif_ops ath10k_usb_hif_ops = {
.tx_sg = ath10k_usb_hif_tx_sg,
};
/* Simulate code to register the callback. */
extern void callback_registration (const void *);
int ath10k_usb_probe(void)
{
callback_registration(&ath10k_usb_hif_ops);
}
/* The original source file ends with:
MODULE_AUTHOR("Atheros Communications, Inc.");
MODULE_DESCRIPTION("Driver support for Qualcomm Atheros 802.11ac WLAN USB devices");
MODULE_LICENSE("Dual BSD/GPL");
*/