summaryrefslogtreecommitdiffstats
path: root/meta/recipes-support/curl/curl/CVE-2020-8231.patch
blob: 51f40047f19e45738907abbfebf4cddf3047cd52 (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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
From c3359693e17fccdf2a04f0b908bc8f51cdc38133 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Mon, 27 Apr 2020 00:33:21 +0200
Subject: [PATCH 1/3] conncache: various concept cleanups

More connection cache accesses are protected by locks.

CONNCACHE_* is a beter prefix for the connection cache lock macros.

Curl_attach_connnection: now called as soon as there's a connection
struct available and before the connection is added to the connection
cache.

Curl_disconnect: now assumes that the connection is already removed from
the connection cache.

Ref: #4915
Closes #5009

Upstream-commit: c06902713998d68202c5a764de910ba8d0e8f54d
Signed-off-by: Kamil Dudka <kdudka@redhat.com>

Upstream-Status: Backport [import from fedora https://koji.fedoraproject.org/koji/fileinfo?rpmID=24270817&filename=0004-curl-7.69.1-CVE-2020-8231.patch ]
CVE: CVE-2020-8286
Signed-off-by: Chee Yang Lee <chee.yang.lee@intel.com>
---
 lib/conncache.c       | 87 ++++++++++++++++++++-----------------------
 lib/conncache.h       |  9 ++---
 lib/hostip.c          | 12 +++---
 lib/http_negotiate.h  |  6 ++-
 lib/http_ntlm.h       |  6 ++-
 lib/multi.c           | 56 ++++++++++++++--------------
 lib/multiif.h         |  1 +
 lib/url.c             | 69 ++++++++++++++++++----------------
 tests/data/test1554   | 14 +++++++
 tests/unit/unit1620.c |  6 +--
 10 files changed, 139 insertions(+), 127 deletions(-)

diff --git a/lib/conncache.c b/lib/conncache.c
index cbd3bb1..95fcea6 100644
--- a/lib/conncache.c
+++ b/lib/conncache.c
@@ -49,53 +49,51 @@ static void conn_llist_dtor(void *user, void *element)
   conn->bundle = NULL;
 }
 
-static CURLcode bundle_create(struct Curl_easy *data,
-                              struct connectbundle **cb_ptr)
+static CURLcode bundle_create(struct connectbundle **bundlep)
 {
-  (void)data;
-  DEBUGASSERT(*cb_ptr == NULL);
-  *cb_ptr = malloc(sizeof(struct connectbundle));
-  if(!*cb_ptr)
+  DEBUGASSERT(*bundlep == NULL);
+  *bundlep = malloc(sizeof(struct connectbundle));
+  if(!*bundlep)
     return CURLE_OUT_OF_MEMORY;
 
-  (*cb_ptr)->num_connections = 0;
-  (*cb_ptr)->multiuse = BUNDLE_UNKNOWN;
+  (*bundlep)->num_connections = 0;
+  (*bundlep)->multiuse = BUNDLE_UNKNOWN;
 
-  Curl_llist_init(&(*cb_ptr)->conn_list, (curl_llist_dtor) conn_llist_dtor);
+  Curl_llist_init(&(*bundlep)->conn_list, (curl_llist_dtor) conn_llist_dtor);
   return CURLE_OK;
 }
 
-static void bundle_destroy(struct connectbundle *cb_ptr)
+static void bundle_destroy(struct connectbundle *bundle)
 {
-  if(!cb_ptr)
+  if(!bundle)
     return;
 
-  Curl_llist_destroy(&cb_ptr->conn_list, NULL);
+  Curl_llist_destroy(&bundle->conn_list, NULL);
 
-  free(cb_ptr);
+  free(bundle);
 }
 
 /* Add a connection to a bundle */
-static void bundle_add_conn(struct connectbundle *cb_ptr,
+static void bundle_add_conn(struct connectbundle *bundle,
                             struct connectdata *conn)
 {
-  Curl_llist_insert_next(&cb_ptr->conn_list, cb_ptr->conn_list.tail, conn,
+  Curl_llist_insert_next(&bundle->conn_list, bundle->conn_list.tail, conn,
                          &conn->bundle_node);
-  conn->bundle = cb_ptr;
-  cb_ptr->num_connections++;
+  conn->bundle = bundle;
+  bundle->num_connections++;
 }
 
 /* Remove a connection from a bundle */
-static int bundle_remove_conn(struct connectbundle *cb_ptr,
+static int bundle_remove_conn(struct connectbundle *bundle,
                               struct connectdata *conn)
 {
   struct curl_llist_element *curr;
 
-  curr = cb_ptr->conn_list.head;
+  curr = bundle->conn_list.head;
   while(curr) {
     if(curr->ptr == conn) {
-      Curl_llist_remove(&cb_ptr->conn_list, curr, NULL);
-      cb_ptr->num_connections--;
+      Curl_llist_remove(&bundle->conn_list, curr, NULL);
+      bundle->num_connections--;
       conn->bundle = NULL;
       return 1; /* we removed a handle */
     }
@@ -162,20 +160,15 @@ static void hashkey(struct connectdata *conn, char *buf,
   msnprintf(buf, len, "%ld%s", port, hostname);
 }
 
-void Curl_conncache_unlock(struct Curl_easy *data)
-{
-  CONN_UNLOCK(data);
-}
-
 /* Returns number of connections currently held in the connection cache.
    Locks/unlocks the cache itself!
 */
 size_t Curl_conncache_size(struct Curl_easy *data)
 {
   size_t num;
-  CONN_LOCK(data);
+  CONNCACHE_LOCK(data);
   num = data->state.conn_cache->num_conn;
-  CONN_UNLOCK(data);
+  CONNCACHE_UNLOCK(data);
   return num;
 }
 
@@ -188,7 +181,7 @@ struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn,
                                                  const char **hostp)
 {
   struct connectbundle *bundle = NULL;
-  CONN_LOCK(conn->data);
+  CONNCACHE_LOCK(conn->data);
   if(connc) {
     char key[HASHKEY_SIZE];
     hashkey(conn, key, sizeof(key), hostp);
@@ -235,8 +228,7 @@ CURLcode Curl_conncache_add_conn(struct conncache *connc,
                                  struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
-  struct connectbundle *bundle;
-  struct connectbundle *new_bundle = NULL;
+  struct connectbundle *bundle = NULL;
   struct Curl_easy *data = conn->data;
 
   /* *find_bundle() locks the connection cache */
@@ -245,20 +237,19 @@ CURLcode Curl_conncache_add_conn(struct conncache *connc,
     int rc;
     char key[HASHKEY_SIZE];
 
-    result = bundle_create(data, &new_bundle);
+    result = bundle_create(&bundle);
     if(result) {
       goto unlock;
     }
 
     hashkey(conn, key, sizeof(key), NULL);
-    rc = conncache_add_bundle(data->state.conn_cache, key, new_bundle);
+    rc = conncache_add_bundle(data->state.conn_cache, key, bundle);
 
     if(!rc) {
-      bundle_destroy(new_bundle);
+      bundle_destroy(bundle);
       result = CURLE_OUT_OF_MEMORY;
       goto unlock;
     }
-    bundle = new_bundle;
   }
 
   bundle_add_conn(bundle, conn);
@@ -270,15 +261,17 @@ CURLcode Curl_conncache_add_conn(struct conncache *connc,
                conn->connection_id, connc->num_conn));
 
   unlock:
-  CONN_UNLOCK(data);
+  CONNCACHE_UNLOCK(data);
 
   return result;
 }
 
 /*
- * Removes the connectdata object from the connection cache *and* clears the
- * ->data pointer association. Pass TRUE/FALSE in the 'lock' argument
- * depending on if the parent function already holds the lock or not.
+ * Removes the connectdata object from the connection cache, but does *not*
+ * clear the conn->data association. The transfer still owns this connection.
+ *
+ * Pass TRUE/FALSE in the 'lock' argument depending on if the parent function
+ * already holds the lock or not.
  */
 void Curl_conncache_remove_conn(struct Curl_easy *data,
                                 struct connectdata *conn, bool lock)
@@ -290,7 +283,7 @@ void Curl_conncache_remove_conn(struct Curl_easy *data,
      due to a failed connection attempt, before being added to a bundle */
   if(bundle) {
     if(lock) {
-      CONN_LOCK(data);
+      CONNCACHE_LOCK(data);
     }
     bundle_remove_conn(bundle, conn);
     if(bundle->num_connections == 0)
@@ -301,9 +294,8 @@ void Curl_conncache_remove_conn(struct Curl_easy *data,
       DEBUGF(infof(data, "The cache now contains %zu members\n",
                    connc->num_conn));
     }
-    conn->data = NULL; /* clear the association */
     if(lock) {
-      CONN_UNLOCK(data);
+      CONNCACHE_UNLOCK(data);
     }
   }
 }
@@ -332,7 +324,7 @@ bool Curl_conncache_foreach(struct Curl_easy *data,
   if(!connc)
     return FALSE;
 
-  CONN_LOCK(data);
+  CONNCACHE_LOCK(data);
   Curl_hash_start_iterate(&connc->hash, &iter);
 
   he = Curl_hash_next_element(&iter);
@@ -350,12 +342,12 @@ bool Curl_conncache_foreach(struct Curl_easy *data,
       curr = curr->next;
 
       if(1 == func(conn, param)) {
-        CONN_UNLOCK(data);
+        CONNCACHE_UNLOCK(data);
         return TRUE;
       }
     }
   }
-  CONN_UNLOCK(data);
+  CONNCACHE_UNLOCK(data);
   return FALSE;
 }
 
@@ -494,7 +486,7 @@ Curl_conncache_extract_oldest(struct Curl_easy *data)
 
   now = Curl_now();
 
-  CONN_LOCK(data);
+  CONNCACHE_LOCK(data);
   Curl_hash_start_iterate(&connc->hash, &iter);
 
   he = Curl_hash_next_element(&iter);
@@ -531,7 +523,7 @@ Curl_conncache_extract_oldest(struct Curl_easy *data)
                  connc->num_conn));
     conn_candidate->data = data; /* associate! */
   }
-  CONN_UNLOCK(data);
+  CONNCACHE_UNLOCK(data);
 
   return conn_candidate;
 }
@@ -548,6 +540,7 @@ void Curl_conncache_close_all_connections(struct conncache *connc)
     sigpipe_ignore(conn->data, &pipe_st);
     /* This will remove the connection from the cache */
     connclose(conn, "kill all");
+    Curl_conncache_remove_conn(conn->data, conn, TRUE);
     (void)Curl_disconnect(connc->closure_handle, conn, FALSE);
     sigpipe_restore(&pipe_st);
 
diff --git a/lib/conncache.h b/lib/conncache.h
index e3e4c9c..3dda21c 100644
--- a/lib/conncache.h
+++ b/lib/conncache.h
@@ -45,21 +45,21 @@ struct conncache {
 #ifdef CURLDEBUG
 /* the debug versions of these macros make extra certain that the lock is
    never doubly locked or unlocked */
-#define CONN_LOCK(x) if((x)->share) {                                   \
+#define CONNCACHE_LOCK(x) if((x)->share) {                              \
     Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE); \
     DEBUGASSERT(!(x)->state.conncache_lock);                            \
     (x)->state.conncache_lock = TRUE;                                   \
   }
 
-#define CONN_UNLOCK(x) if((x)->share) {                                 \
+#define CONNCACHE_UNLOCK(x) if((x)->share) {                            \
     DEBUGASSERT((x)->state.conncache_lock);                             \
     (x)->state.conncache_lock = FALSE;                                  \
     Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT);                     \
   }
 #else
-#define CONN_LOCK(x) if((x)->share)                                     \
+#define CONNCACHE_LOCK(x) if((x)->share)                                \
     Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE)
-#define CONN_UNLOCK(x) if((x)->share)                   \
+#define CONNCACHE_UNLOCK(x) if((x)->share)              \
     Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT)
 #endif
 
@@ -77,7 +77,6 @@ void Curl_conncache_destroy(struct conncache *connc);
 struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn,
                                                  struct conncache *connc,
                                                  const char **hostp);
-void Curl_conncache_unlock(struct Curl_easy *data);
 /* returns number of connections currently held in the connection cache */
 size_t Curl_conncache_size(struct Curl_easy *data);
 
diff --git a/lib/hostip.c b/lib/hostip.c
index c0feb79..f5bb634 100644
--- a/lib/hostip.c
+++ b/lib/hostip.c
@@ -1085,10 +1085,12 @@ CURLcode Curl_once_resolved(struct connectdata *conn,
 
   result = Curl_setup_conn(conn, protocol_done);
 
-  if(result)
-    /* We're not allowed to return failure with memory left allocated
-       in the connectdata struct, free those here */
-    Curl_disconnect(conn->data, conn, TRUE); /* close the connection */
-
+  if(result) {
+    struct Curl_easy *data = conn->data;
+    DEBUGASSERT(data);
+    Curl_detach_connnection(data);
+    Curl_conncache_remove_conn(data, conn, TRUE);
+    Curl_disconnect(data, conn, TRUE);
+  }
   return result;
 }
diff --git a/lib/http_negotiate.h b/lib/http_negotiate.h
index 4f0ac16..a737f6f 100644
--- a/lib/http_negotiate.h
+++ b/lib/http_negotiate.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -33,6 +33,8 @@ CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy);
 
 void Curl_http_auth_cleanup_negotiate(struct connectdata *conn);
 
-#endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */
+#else /* !CURL_DISABLE_HTTP && USE_SPNEGO */
+#define Curl_http_auth_cleanup_negotiate(x)
+#endif
 
 #endif /* HEADER_CURL_HTTP_NEGOTIATE_H */
diff --git a/lib/http_ntlm.h b/lib/http_ntlm.h
index 003714d..3ebdf97 100644
--- a/lib/http_ntlm.h
+++ b/lib/http_ntlm.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -35,6 +35,8 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy);
 
 void Curl_http_auth_cleanup_ntlm(struct connectdata *conn);
 
-#endif /* !CURL_DISABLE_HTTP && USE_NTLM */
+#else /* !CURL_DISABLE_HTTP && USE_NTLM */
+#define Curl_http_auth_cleanup_ntlm(x)
+#endif
 
 #endif /* HEADER_CURL_HTTP_NTLM_H */
diff --git a/lib/multi.c b/lib/multi.c
index e10e752..273653d 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -79,7 +79,6 @@ static CURLMcode add_next_timeout(struct curltime now,
 static CURLMcode multi_timeout(struct Curl_multi *multi,
                                long *timeout_ms);
 static void process_pending_handles(struct Curl_multi *multi);
-static void detach_connnection(struct Curl_easy *data);
 
 #ifdef DEBUGBUILD
 static const char * const statename[]={
@@ -112,7 +111,7 @@ static void Curl_init_completed(struct Curl_easy *data)
 
   /* Important: reset the conn pointer so that we don't point to memory
      that could be freed anytime */
-  detach_connnection(data);
+  Curl_detach_connnection(data);
   Curl_expire_clear(data); /* stop all timers */
 }
 
@@ -506,6 +505,7 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
      easy handle is added */
   memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall));
 
+  CONNCACHE_LOCK(data);
   /* The closure handle only ever has default timeouts set. To improve the
      state somewhat we clone the timeouts from each added handle so that the
      closure handle always has the same timeouts as the most recently added
@@ -515,6 +515,7 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
     data->set.server_response_timeout;
   data->state.conn_cache->closure_handle->set.no_signal =
     data->set.no_signal;
+  CONNCACHE_UNLOCK(data);
 
   Curl_update_timer(multi);
   return CURLM_OK;
@@ -589,14 +590,14 @@ static CURLcode multi_done(struct Curl_easy *data,
 
   process_pending_handles(data->multi); /* connection / multiplex */
 
-  CONN_LOCK(data);
-  detach_connnection(data);
+  CONNCACHE_LOCK(data);
+  Curl_detach_connnection(data);
   if(CONN_INUSE(conn)) {
     /* Stop if still used. */
     /* conn->data must not remain pointing to this transfer since it is going
        away! Find another to own it! */
     conn->data = conn->easyq.head->ptr;
-    CONN_UNLOCK(data);
+    CONNCACHE_UNLOCK(data);
     DEBUGF(infof(data, "Connection still in use %zu, "
                  "no more multi_done now!\n",
                  conn->easyq.size));
@@ -647,7 +648,8 @@ static CURLcode multi_done(struct Curl_easy *data,
        || (premature && !(conn->handler->flags & PROTOPT_STREAM))) {
     CURLcode res2;
     connclose(conn, "disconnecting");
-    CONN_UNLOCK(data);
+    Curl_conncache_remove_conn(data, conn, FALSE);
+    CONNCACHE_UNLOCK(data);
     res2 = Curl_disconnect(data, conn, premature);
 
     /* If we had an error already, make sure we return that one. But
@@ -666,7 +668,7 @@ static CURLcode multi_done(struct Curl_easy *data,
               conn->bits.conn_to_host ? conn->conn_to_host.dispname :
               conn->host.dispname);
     /* the connection is no longer in use by this transfer */
-    CONN_UNLOCK(data);
+    CONNCACHE_UNLOCK(data);
     if(Curl_conncache_return_conn(data, conn)) {
       /* remember the most recently used connection */
       data->state.lastconnect = conn;
@@ -774,8 +776,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
                                 vanish with this handle */
 
   /* Remove the association between the connection and the handle */
-  if(data->conn)
-    detach_connnection(data);
+  Curl_detach_connnection(data);
 
 #ifdef USE_LIBPSL
   /* Remove the PSL association. */
@@ -824,9 +825,13 @@ bool Curl_multiplex_wanted(const struct Curl_multi *multi)
   return (multi && (multi->multiplexing));
 }
 
-/* This is the only function that should clear data->conn. This will
-   occasionally be called with the pointer already cleared. */
-static void detach_connnection(struct Curl_easy *data)
+/*
+ * Curl_detach_connnection() removes the given transfer from the connection.
+ *
+ * This is the only function that should clear data->conn. This will
+ * occasionally be called with the data->conn pointer already cleared.
+ */
+void Curl_detach_connnection(struct Curl_easy *data)
 {
   struct connectdata *conn = data->conn;
   if(conn)
@@ -834,7 +839,11 @@ static void detach_connnection(struct Curl_easy *data)
   data->conn = NULL;
 }
 
-/* This is the only function that should assign data->conn */
+/*
+ * Curl_attach_connnection() attaches this transfer to this connection.
+ *
+ * This is the only function that should assign data->conn
+ */
 void Curl_attach_connnection(struct Curl_easy *data,
                              struct connectdata *conn)
 {
@@ -1536,19 +1545,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
     bool stream_error = FALSE;
     rc = CURLM_OK;
 
-    DEBUGASSERT((data->mstate <= CURLM_STATE_CONNECT) ||
-                (data->mstate >= CURLM_STATE_DONE) ||
-                data->conn);
-    if(!data->conn &&
-       data->mstate > CURLM_STATE_CONNECT &&
-       data->mstate < CURLM_STATE_DONE) {
-      /* In all these states, the code will blindly access 'data->conn'
-         so this is precaution that it isn't NULL. And it silences static
-         analyzers. */
-      failf(data, "In state %d with no conn, bail out!\n", data->mstate);
-      return CURLM_INTERNAL_ERROR;
-    }
-
     if(multi_ischanged(multi, TRUE)) {
       DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue!\n"));
       process_pending_handles(multi); /* multiplexed */
@@ -2231,8 +2227,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
          * access free'd data, if the connection is free'd and the handle
          * removed before we perform the processing in CURLM_STATE_COMPLETED
          */
-        if(data->conn)
-          detach_connnection(data);
+        Curl_detach_connnection(data);
       }
 
 #ifndef CURL_DISABLE_FTP
@@ -2284,7 +2279,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
             /* This is where we make sure that the conn pointer is reset.
                We don't have to do this in every case block above where a
                failure is detected */
-            detach_connnection(data);
+            Curl_detach_connnection(data);
+
+            /* remove connection from cache */
+            Curl_conncache_remove_conn(data, conn, TRUE);
 
             /* disconnect properly */
             Curl_disconnect(data, conn, dead_connection);
diff --git a/lib/multiif.h b/lib/multiif.h
index bde755e..c07587b 100644
--- a/lib/multiif.h
+++ b/lib/multiif.h
@@ -33,6 +33,7 @@ void Curl_expire_done(struct Curl_easy *data, expire_id id);
 void Curl_update_timer(struct Curl_multi *multi);
 void Curl_attach_connnection(struct Curl_easy *data,
                              struct connectdata *conn);
+void Curl_detach_connnection(struct Curl_easy *data);
 bool Curl_multiplex_wanted(const struct Curl_multi *multi);
 void Curl_set_in_callback(struct Curl_easy *data, bool value);
 bool Curl_is_in_callback(struct Curl_easy *easy);
diff --git a/lib/url.c b/lib/url.c
index a826f8a..4ed0623 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -679,9 +679,7 @@ static void conn_reset_all_postponed_data(struct connectdata *conn)
 
 static void conn_shutdown(struct connectdata *conn)
 {
-  if(!conn)
-    return;
-
+  DEBUGASSERT(conn);
   infof(conn->data, "Closing connection %ld\n", conn->connection_id);
   DEBUGASSERT(conn->data);
 
@@ -702,16 +700,11 @@ static void conn_shutdown(struct connectdata *conn)
     Curl_closesocket(conn, conn->tempsock[0]);
   if(CURL_SOCKET_BAD != conn->tempsock[1])
     Curl_closesocket(conn, conn->tempsock[1]);
-
-  /* unlink ourselves. this should be called last since other shutdown
-     procedures need a valid conn->data and this may clear it. */
-  Curl_conncache_remove_conn(conn->data, conn, TRUE);
 }
 
 static void conn_free(struct connectdata *conn)
 {
-  if(!conn)
-    return;
+  DEBUGASSERT(conn);
 
   Curl_free_idnconverted_hostname(&conn->host);
   Curl_free_idnconverted_hostname(&conn->conn_to_host);
@@ -778,13 +771,17 @@ static void conn_free(struct connectdata *conn)
 CURLcode Curl_disconnect(struct Curl_easy *data,
                          struct connectdata *conn, bool dead_connection)
 {
-  if(!conn)
-    return CURLE_OK; /* this is closed and fine already */
+  /* there must be a connection to close */
+  DEBUGASSERT(conn);
 
-  if(!data) {
-    DEBUGF(infof(data, "DISCONNECT without easy handle, ignoring\n"));
-    return CURLE_OK;
-  }
+  /* it must be removed from the connection cache */
+  DEBUGASSERT(!conn->bundle);
+
+  /* there must be an associated transfer */
+  DEBUGASSERT(data);
+
+  /* the transfer must be detached from the connection */
+  DEBUGASSERT(!data->conn);
 
   /*
    * If this connection isn't marked to force-close, leave it open if there
@@ -800,16 +797,11 @@ CURLcode Curl_disconnect(struct Curl_easy *data,
     conn->dns_entry = NULL;
   }
 
-  Curl_hostcache_prune(data); /* kill old DNS cache entries */
-
-#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM)
   /* Cleanup NTLM connection-related data */
   Curl_http_auth_cleanup_ntlm(conn);
-#endif
-#if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO)
+
   /* Cleanup NEGOTIATE connection-related data */
   Curl_http_auth_cleanup_negotiate(conn);
-#endif
 
   /* the protocol specific disconnect handler and conn_shutdown need a transfer
      for the connection! */
@@ -1006,8 +998,12 @@ static int call_extract_if_dead(struct connectdata *conn, void *param)
 static void prune_dead_connections(struct Curl_easy *data)
 {
   struct curltime now = Curl_now();
-  timediff_t elapsed =
+  timediff_t elapsed;
+
+  CONNCACHE_LOCK(data);
+  elapsed =
     Curl_timediff(now, data->state.conn_cache->last_cleanup);
+  CONNCACHE_UNLOCK(data);
 
   if(elapsed >= 1000L) {
     struct prunedead prune;
@@ -1015,10 +1011,17 @@ static void prune_dead_connections(struct Curl_easy *data)
     prune.extracted = NULL;
     while(Curl_conncache_foreach(data, data->state.conn_cache, &prune,
                                  call_extract_if_dead)) {
+      /* unlocked */
+
+      /* remove connection from cache */
+      Curl_conncache_remove_conn(data, prune.extracted, TRUE);
+
       /* disconnect it */
       (void)Curl_disconnect(data, prune.extracted, /* dead_connection */TRUE);
     }
+    CONNCACHE_LOCK(data);
     data->state.conn_cache->last_cleanup = now;
+    CONNCACHE_UNLOCK(data);
   }
 }
 
@@ -1078,7 +1081,7 @@ ConnectionExists(struct Curl_easy *data,
         if(data->set.pipewait) {
           infof(data, "Server doesn't support multiplex yet, wait\n");
           *waitpipe = TRUE;
-          Curl_conncache_unlock(data);
+          CONNCACHE_UNLOCK(data);
           return FALSE; /* no re-use */
         }
 
@@ -1402,11 +1405,12 @@ ConnectionExists(struct Curl_easy *data,
   if(chosen) {
     /* mark it as used before releasing the lock */
     chosen->data = data; /* own it! */
-    Curl_conncache_unlock(data);
+    Curl_attach_connnection(data, chosen);
+    CONNCACHE_UNLOCK(data);
     *usethis = chosen;
     return TRUE; /* yes, we found one to use! */
   }
-  Curl_conncache_unlock(data);
+  CONNCACHE_UNLOCK(data);
 
   if(foundPendingCandidate && data->set.pipewait) {
     infof(data,
@@ -3519,6 +3523,7 @@ static CURLcode create_conn(struct Curl_easy *data,
     if(!result) {
       conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; /* we are "connected */
 
+      Curl_attach_connnection(data, conn);
       result = Curl_conncache_add_conn(data->state.conn_cache, conn);
       if(result)
         goto out;
@@ -3533,7 +3538,6 @@ static CURLcode create_conn(struct Curl_easy *data,
         (void)conn->handler->done(conn, result, FALSE);
         goto out;
       }
-      Curl_attach_connnection(data, conn);
       Curl_setup_transfer(data, -1, -1, FALSE, -1);
     }
 
@@ -3683,7 +3687,7 @@ static CURLcode create_conn(struct Curl_easy *data,
 
         /* The bundle is full. Extract the oldest connection. */
         conn_candidate = Curl_conncache_extract_bundle(data, bundle);
-        Curl_conncache_unlock(data);
+        CONNCACHE_UNLOCK(data);
 
         if(conn_candidate)
           (void)Curl_disconnect(data, conn_candidate,
@@ -3695,7 +3699,7 @@ static CURLcode create_conn(struct Curl_easy *data,
         }
       }
       else
-        Curl_conncache_unlock(data);
+        CONNCACHE_UNLOCK(data);
 
     }
 
@@ -3729,6 +3733,8 @@ static CURLcode create_conn(struct Curl_easy *data,
        * This is a brand new connection, so let's store it in the connection
        * cache of ours!
        */
+      Curl_attach_connnection(data, conn);
+
       result = Curl_conncache_add_conn(data->state.conn_cache, conn);
       if(result)
         goto out;
@@ -3883,7 +3889,7 @@ CURLcode Curl_connect(struct Curl_easy *data,
   result = create_conn(data, &conn, asyncp);
 
   if(!result) {
-    if(CONN_INUSE(conn))
+    if(CONN_INUSE(conn) > 1)
       /* multiplexed */
       *protocol_done = TRUE;
     else if(!*asyncp) {
@@ -3900,11 +3906,10 @@ CURLcode Curl_connect(struct Curl_easy *data,
   else if(result && conn) {
     /* We're not allowed to return failure with memory left allocated in the
        connectdata struct, free those here */
+    Curl_detach_connnection(data);
+    Curl_conncache_remove_conn(data, conn, TRUE);
     Curl_disconnect(data, conn, TRUE);
   }
-  else if(!result && !data->conn)
-    /* FILE: transfers already have the connection attached */
-    Curl_attach_connnection(data, conn);
 
   return result;
 }
diff --git a/tests/data/test1554 b/tests/data/test1554
index 06f1897..d3926d9 100644
--- a/tests/data/test1554
+++ b/tests/data/test1554
@@ -29,6 +29,12 @@ run 1: foobar and so on fun!
 <- Mutex unlock
 -> Mutex lock
 <- Mutex unlock
+-> Mutex lock
+<- Mutex unlock
+-> Mutex lock
+<- Mutex unlock
+-> Mutex lock
+<- Mutex unlock
 run 1: foobar and so on fun!
 -> Mutex lock
 <- Mutex unlock
@@ -40,6 +46,10 @@ run 1: foobar and so on fun!
 <- Mutex unlock
 -> Mutex lock
 <- Mutex unlock
+-> Mutex lock
+<- Mutex unlock
+-> Mutex lock
+<- Mutex unlock
 run 1: foobar and so on fun!
 -> Mutex lock
 <- Mutex unlock
@@ -51,6 +61,10 @@ run 1: foobar and so on fun!
 <- Mutex unlock
 -> Mutex lock
 <- Mutex unlock
+-> Mutex lock
+<- Mutex unlock
+-> Mutex lock
+<- Mutex unlock
 run 1: foobar and so on fun!
 -> Mutex lock
 <- Mutex unlock
diff --git a/tests/unit/unit1620.c b/tests/unit/unit1620.c
index 6e572c6..b23e5b9 100644
--- a/tests/unit/unit1620.c
+++ b/tests/unit/unit1620.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -73,10 +73,6 @@ UNITTEST_START
   fail_unless(rc == CURLE_OK,
               "Curl_parse_login_details() failed");
 
-  rc = Curl_disconnect(empty, empty->conn, FALSE);
-  fail_unless(rc == CURLE_OK,
-              "Curl_disconnect() with dead_connection set FALSE failed");
-
   Curl_freeset(empty);
   for(i = (enum dupstring)0; i < STRING_LAST; i++) {
     fail_unless(empty->set.str[i] == NULL,
-- 
2.25.4


From 6830828c9eecd9ab14404f2f49f19b56dec62130 Mon Sep 17 00:00:00 2001
From: Marc Aldorasi <marc@groundctl.com>
Date: Thu, 30 Jul 2020 14:16:17 -0400
Subject: [PATCH 2/3] multi_remove_handle: close unused connect-only
 connections

Previously any connect-only connections in a multi handle would be kept
alive until the multi handle was closed.  Since these connections cannot
be re-used, they can be marked for closure when the associated easy
handle is removed from the multi handle.

Closes #5749

Upstream-commit: d5bb459ccf1fc5980ae4b95c05b4ecf6454a7599
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
 lib/multi.c         | 34 ++++++++++++++++++++++++++++++----
 tests/data/test1554 |  6 ++++++
 2 files changed, 36 insertions(+), 4 deletions(-)

diff --git a/lib/multi.c b/lib/multi.c
index 249e360..f1371bd 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -682,6 +682,26 @@ static CURLcode multi_done(struct Curl_easy *data,
   return result;
 }
 
+static int close_connect_only(struct connectdata *conn, void *param)
+{
+  struct Curl_easy *data = param;
+
+  if(data->state.lastconnect != conn)
+    return 0;
+
+  if(conn->data != data)
+    return 1;
+  conn->data = NULL;
+
+  if(!conn->bits.connect_only)
+    return 1;
+
+  connclose(conn, "Removing connect-only easy handle");
+  conn->bits.connect_only = FALSE;
+
+  return 1;
+}
+
 CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
                                    struct Curl_easy *data)
 {
@@ -765,10 +785,6 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
      multi_done() as that may actually call Curl_expire that uses this */
   Curl_llist_destroy(&data->state.timeoutlist, NULL);
 
-  /* as this was using a shared connection cache we clear the pointer to that
-     since we're not part of that multi handle anymore */
-  data->state.conn_cache = NULL;
-
   /* change state without using multistate(), only to make singlesocket() do
      what we want */
   data->mstate = CURLM_STATE_COMPLETED;
@@ -778,12 +794,22 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
   /* Remove the association between the connection and the handle */
   Curl_detach_connnection(data);
 
+  if(data->state.lastconnect) {
+    /* Mark any connect-only connection for closure */
+    Curl_conncache_foreach(data, data->state.conn_cache,
+                           data, &close_connect_only);
+  }
+
 #ifdef USE_LIBPSL
   /* Remove the PSL association. */
   if(data->psl == &multi->psl)
     data->psl = NULL;
 #endif
 
+  /* as this was using a shared connection cache we clear the pointer to that
+     since we're not part of that multi handle anymore */
+  data->state.conn_cache = NULL;
+
   data->multi = NULL; /* clear the association to this multi handle */
 
   /* make sure there's no pending message in the queue sent from this easy
diff --git a/tests/data/test1554 b/tests/data/test1554
index d3926d9..fffa6ad 100644
--- a/tests/data/test1554
+++ b/tests/data/test1554
@@ -50,6 +50,8 @@ run 1: foobar and so on fun!
 <- Mutex unlock
 -> Mutex lock
 <- Mutex unlock
+-> Mutex lock
+<- Mutex unlock
 run 1: foobar and so on fun!
 -> Mutex lock
 <- Mutex unlock
@@ -65,6 +67,8 @@ run 1: foobar and so on fun!
 <- Mutex unlock
 -> Mutex lock
 <- Mutex unlock
+-> Mutex lock
+<- Mutex unlock
 run 1: foobar and so on fun!
 -> Mutex lock
 <- Mutex unlock
@@ -74,6 +78,8 @@ run 1: foobar and so on fun!
 <- Mutex unlock
 -> Mutex lock
 <- Mutex unlock
+-> Mutex lock
+<- Mutex unlock
 </datacheck>
 </reply>
 
-- 
2.25.4


From 01148ee40dd913a169435b0f9ea90e6393821e70 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Sun, 16 Aug 2020 11:34:35 +0200
Subject: [PATCH 3/3] Curl_easy: remember last connection by id, not by pointer

CVE-2020-8231

Bug: https://curl.haxx.se/docs/CVE-2020-8231.html

Reported-by: Marc Aldorasi
Closes #5824

Upstream-commit: 3c9e021f86872baae412a427e807fbfa2f3e8a22
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
 lib/connect.c | 19 ++++++++++---------
 lib/easy.c    |  3 +--
 lib/multi.c   |  9 +++++----
 lib/url.c     |  2 +-
 lib/urldata.h |  2 +-
 5 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/lib/connect.c b/lib/connect.c
index 29293f0..e1c5662 100644
--- a/lib/connect.c
+++ b/lib/connect.c
@@ -1356,15 +1356,15 @@ CURLcode Curl_connecthost(struct connectdata *conn,  /* context */
 }
 
 struct connfind {
-  struct connectdata *tofind;
-  bool found;
+  long id_tofind;
+  struct connectdata *found;
 };
 
 static int conn_is_conn(struct connectdata *conn, void *param)
 {
   struct connfind *f = (struct connfind *)param;
-  if(conn == f->tofind) {
-    f->found = TRUE;
+  if(conn->connection_id == f->id_tofind) {
+    f->found = conn;
     return 1;
   }
   return 0;
@@ -1386,21 +1386,22 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
    * - that is associated with a multi handle, and whose connection
    *   was detached with CURLOPT_CONNECT_ONLY
    */
-  if(data->state.lastconnect && (data->multi_easy || data->multi)) {
-    struct connectdata *c = data->state.lastconnect;
+  if((data->state.lastconnect_id != -1) && (data->multi_easy || data->multi)) {
+    struct connectdata *c;
     struct connfind find;
-    find.tofind = data->state.lastconnect;
-    find.found = FALSE;
+    find.id_tofind = data->state.lastconnect_id;
+    find.found = NULL;
 
     Curl_conncache_foreach(data, data->multi_easy?
                            &data->multi_easy->conn_cache:
                            &data->multi->conn_cache, &find, conn_is_conn);
 
     if(!find.found) {
-      data->state.lastconnect = NULL;
+      data->state.lastconnect_id = -1;
       return CURL_SOCKET_BAD;
     }
 
+    c = find.found;
     if(connp) {
       /* only store this if the caller cares for it */
       *connp = c;
diff --git a/lib/easy.c b/lib/easy.c
index 292cca7..a69eb9e 100644
--- a/lib/easy.c
+++ b/lib/easy.c
@@ -831,8 +831,7 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
 
   /* the connection cache is setup on demand */
   outcurl->state.conn_cache = NULL;
-
-  outcurl->state.lastconnect = NULL;
+  outcurl->state.lastconnect_id = -1;
 
   outcurl->progress.flags    = data->progress.flags;
   outcurl->progress.callback = data->progress.callback;
diff --git a/lib/multi.c b/lib/multi.c
index f1371bd..778c537 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -453,6 +453,7 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
     data->state.conn_cache = &data->share->conn_cache;
   else
     data->state.conn_cache = &multi->conn_cache;
+  data->state.lastconnect_id = -1;
 
 #ifdef USE_LIBPSL
   /* Do the same for PSL. */
@@ -671,11 +672,11 @@ static CURLcode multi_done(struct Curl_easy *data,
     CONNCACHE_UNLOCK(data);
     if(Curl_conncache_return_conn(data, conn)) {
       /* remember the most recently used connection */
-      data->state.lastconnect = conn;
+      data->state.lastconnect_id = conn->connection_id;
       infof(data, "%s\n", buffer);
     }
     else
-      data->state.lastconnect = NULL;
+      data->state.lastconnect_id = -1;
   }
 
   Curl_free_request_state(data);
@@ -686,7 +687,7 @@ static int close_connect_only(struct connectdata *conn, void *param)
 {
   struct Curl_easy *data = param;
 
-  if(data->state.lastconnect != conn)
+  if(data->state.lastconnect_id != conn->connection_id)
     return 0;
 
   if(conn->data != data)
@@ -794,7 +795,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
   /* Remove the association between the connection and the handle */
   Curl_detach_connnection(data);
 
-  if(data->state.lastconnect) {
+  if(data->state.lastconnect_id != -1) {
     /* Mark any connect-only connection for closure */
     Curl_conncache_foreach(data, data->state.conn_cache,
                            data, &close_connect_only);
diff --git a/lib/url.c b/lib/url.c
index a1a6b69..2919a3d 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -617,7 +617,7 @@ CURLcode Curl_open(struct Curl_easy **curl)
       Curl_initinfo(data);
 
       /* most recent connection is not yet defined */
-      data->state.lastconnect = NULL;
+      data->state.lastconnect_id = -1;
 
       data->progress.flags |= PGRS_HIDE;
       data->state.current_speed = -1; /* init to negative == impossible */
diff --git a/lib/urldata.h b/lib/urldata.h
index f80a02d..6d8eb69 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1332,7 +1332,7 @@ struct UrlState {
   /* buffers to store authentication data in, as parsed from input options */
   struct curltime keeps_speed; /* for the progress meter really */
 
-  struct connectdata *lastconnect; /* The last connection, NULL if undefined */
+  long lastconnect_id; /* The last connection, -1 if undefined */
 
   char *headerbuff; /* allocated buffer to store headers in */
   size_t headersize;   /* size of the allocation */
-- 
2.25.4