aboutsummaryrefslogtreecommitdiff
path: root/gcc/testsuite/gcc.dg/analyzer/attr-malloc-CVE-2019-19078-usb-leak.c
blob: 905d50ec3f9598e5b9a5ec85bc29a4c6b85485f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/* 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;
		}

		/* TODO: the loop confuses the double-free checker (another
		   instance of PR analyzer/93695).  */
		usb_free_urb(urb); /* { dg-bogus "double-'usb_free_urb' of 'urb'" "" { xfail *-*-* } } */
	}

	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");
*/