diff options
| author | David S. Miller <davem@davemloft.net> | 2019-04-10 16:07:02 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2019-04-10 16:07:02 -0400 |
| commit | 44f5e0480752b356f577aef90a5813299c3c60b1 (patch) | |
| tree | 745bb98ce86c2b200e8d2c642ddcf65c4cb9a2ef /net | |
| parent | f4a58857151f5d0a885fcebb47f6fb1b17dfda9a (diff) | |
| parent | 4a9c2e3746e6151fd5d077259d79ce9ca86d47d7 (diff) | |
Merge branch 'tls-leaks'
Jakub Kicinski says:
====================
net: tls: fix memory leaks and freeing skbs
This series fixes two memory issues and a stack overflow.
First two patches are fairly simple leaks. Third patch
partially reverts an optimization made to the strparser
which causes creation of skb->frag_list->skb->frag_list...
chains of 100s of skbs, leading to recursive kfree_skb()
filling up the kernel stack.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
| -rw-r--r-- | net/strparser/strparser.c | 12 | ||||
| -rw-r--r-- | net/tls/tls_device.c | 12 | ||||
| -rw-r--r-- | net/tls/tls_main.c | 22 | ||||
| -rw-r--r-- | net/tls/tls_sw.c | 15 |
4 files changed, 39 insertions, 22 deletions
diff --git a/net/strparser/strparser.c b/net/strparser/strparser.c index 860dcfb95ee4..fa6c977b4c41 100644 --- a/net/strparser/strparser.c +++ b/net/strparser/strparser.c | |||
| @@ -140,13 +140,11 @@ static int __strp_recv(read_descriptor_t *desc, struct sk_buff *orig_skb, | |||
| 140 | /* We are going to append to the frags_list of head. | 140 | /* We are going to append to the frags_list of head. |
| 141 | * Need to unshare the frag_list. | 141 | * Need to unshare the frag_list. |
| 142 | */ | 142 | */ |
| 143 | if (skb_has_frag_list(head)) { | 143 | err = skb_unclone(head, GFP_ATOMIC); |
| 144 | err = skb_unclone(head, GFP_ATOMIC); | 144 | if (err) { |
| 145 | if (err) { | 145 | STRP_STATS_INCR(strp->stats.mem_fail); |
| 146 | STRP_STATS_INCR(strp->stats.mem_fail); | 146 | desc->error = err; |
| 147 | desc->error = err; | 147 | return 0; |
| 148 | return 0; | ||
| 149 | } | ||
| 150 | } | 148 | } |
| 151 | 149 | ||
| 152 | if (unlikely(skb_shinfo(head)->frag_list)) { | 150 | if (unlikely(skb_shinfo(head)->frag_list)) { |
diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index 135a7ee9db03..9f3bdbc1e593 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c | |||
| @@ -52,8 +52,11 @@ static DEFINE_SPINLOCK(tls_device_lock); | |||
| 52 | 52 | ||
| 53 | static void tls_device_free_ctx(struct tls_context *ctx) | 53 | static void tls_device_free_ctx(struct tls_context *ctx) |
| 54 | { | 54 | { |
| 55 | if (ctx->tx_conf == TLS_HW) | 55 | if (ctx->tx_conf == TLS_HW) { |
| 56 | kfree(tls_offload_ctx_tx(ctx)); | 56 | kfree(tls_offload_ctx_tx(ctx)); |
| 57 | kfree(ctx->tx.rec_seq); | ||
| 58 | kfree(ctx->tx.iv); | ||
| 59 | } | ||
| 57 | 60 | ||
| 58 | if (ctx->rx_conf == TLS_HW) | 61 | if (ctx->rx_conf == TLS_HW) |
| 59 | kfree(tls_offload_ctx_rx(ctx)); | 62 | kfree(tls_offload_ctx_rx(ctx)); |
| @@ -216,6 +219,13 @@ void tls_device_sk_destruct(struct sock *sk) | |||
| 216 | } | 219 | } |
| 217 | EXPORT_SYMBOL(tls_device_sk_destruct); | 220 | EXPORT_SYMBOL(tls_device_sk_destruct); |
| 218 | 221 | ||
| 222 | void tls_device_free_resources_tx(struct sock *sk) | ||
| 223 | { | ||
| 224 | struct tls_context *tls_ctx = tls_get_ctx(sk); | ||
| 225 | |||
| 226 | tls_free_partial_record(sk, tls_ctx); | ||
| 227 | } | ||
| 228 | |||
| 219 | static void tls_append_frag(struct tls_record_info *record, | 229 | static void tls_append_frag(struct tls_record_info *record, |
| 220 | struct page_frag *pfrag, | 230 | struct page_frag *pfrag, |
| 221 | int size) | 231 | int size) |
diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c index df921a2904b9..a3cca1ef0098 100644 --- a/net/tls/tls_main.c +++ b/net/tls/tls_main.c | |||
| @@ -208,6 +208,26 @@ int tls_push_partial_record(struct sock *sk, struct tls_context *ctx, | |||
| 208 | return tls_push_sg(sk, ctx, sg, offset, flags); | 208 | return tls_push_sg(sk, ctx, sg, offset, flags); |
| 209 | } | 209 | } |
| 210 | 210 | ||
| 211 | bool tls_free_partial_record(struct sock *sk, struct tls_context *ctx) | ||
| 212 | { | ||
| 213 | struct scatterlist *sg; | ||
| 214 | |||
| 215 | sg = ctx->partially_sent_record; | ||
| 216 | if (!sg) | ||
| 217 | return false; | ||
| 218 | |||
| 219 | while (1) { | ||
| 220 | put_page(sg_page(sg)); | ||
| 221 | sk_mem_uncharge(sk, sg->length); | ||
| 222 | |||
| 223 | if (sg_is_last(sg)) | ||
| 224 | break; | ||
| 225 | sg++; | ||
| 226 | } | ||
| 227 | ctx->partially_sent_record = NULL; | ||
| 228 | return true; | ||
| 229 | } | ||
| 230 | |||
| 211 | static void tls_write_space(struct sock *sk) | 231 | static void tls_write_space(struct sock *sk) |
| 212 | { | 232 | { |
| 213 | struct tls_context *ctx = tls_get_ctx(sk); | 233 | struct tls_context *ctx = tls_get_ctx(sk); |
| @@ -267,6 +287,8 @@ static void tls_sk_proto_close(struct sock *sk, long timeout) | |||
| 267 | kfree(ctx->tx.rec_seq); | 287 | kfree(ctx->tx.rec_seq); |
| 268 | kfree(ctx->tx.iv); | 288 | kfree(ctx->tx.iv); |
| 269 | tls_sw_free_resources_tx(sk); | 289 | tls_sw_free_resources_tx(sk); |
| 290 | } else if (ctx->tx_conf == TLS_HW) { | ||
| 291 | tls_device_free_resources_tx(sk); | ||
| 270 | } | 292 | } |
| 271 | 293 | ||
| 272 | if (ctx->rx_conf == TLS_SW) { | 294 | if (ctx->rx_conf == TLS_SW) { |
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 20b191227969..b50ced862f6f 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c | |||
| @@ -2052,20 +2052,7 @@ void tls_sw_free_resources_tx(struct sock *sk) | |||
| 2052 | /* Free up un-sent records in tx_list. First, free | 2052 | /* Free up un-sent records in tx_list. First, free |
| 2053 | * the partially sent record if any at head of tx_list. | 2053 | * the partially sent record if any at head of tx_list. |
| 2054 | */ | 2054 | */ |
| 2055 | if (tls_ctx->partially_sent_record) { | 2055 | if (tls_free_partial_record(sk, tls_ctx)) { |
| 2056 | struct scatterlist *sg = tls_ctx->partially_sent_record; | ||
| 2057 | |||
| 2058 | while (1) { | ||
| 2059 | put_page(sg_page(sg)); | ||
| 2060 | sk_mem_uncharge(sk, sg->length); | ||
| 2061 | |||
| 2062 | if (sg_is_last(sg)) | ||
| 2063 | break; | ||
| 2064 | sg++; | ||
| 2065 | } | ||
| 2066 | |||
| 2067 | tls_ctx->partially_sent_record = NULL; | ||
| 2068 | |||
| 2069 | rec = list_first_entry(&ctx->tx_list, | 2056 | rec = list_first_entry(&ctx->tx_list, |
| 2070 | struct tls_rec, list); | 2057 | struct tls_rec, list); |
| 2071 | list_del(&rec->list); | 2058 | list_del(&rec->list); |
