CVE-2026-9080: UAF after pause in socket callback
What the report claims
Heap-use-after-free in lib/multi_ev.c when curl_easy_pause() is called from a CURLMOPT_SOCKETFUNCTION callback, leading to UAF writes to entry->announced and entry->action after the entry may have been freed.
Compare with curl's recorded outcome
Known vulnerability
A real vulnerability: CVE-2026-9080 UAF after pause in socket callback
Shown for comparison after analysis. The pipeline did not see this label.
ProveIt's finding
The available evidence does not settle the report. A maintainer should investigate.
See the withdrawn model reasoning
The following reasoning failed the citation check and does not support the final finding.
- mev_sh_entry_update holds entry pointer across socket_cb call (lines 272-273)
- curl_easy_pause() is documented as callable from callbacks and re-enters mev_assess()
- mev_assess() has no guard on multi->in_callback
- mev_pollset_diff() can free the entry via mev_forget_socket() -> mev_sh_entry_kill()
- Lines 275 and 280 write to the freed entry after callback returns
- The reproducer demonstrates a concrete trigger path from attacker-controlled input
How this analysis ran 5 claims · 14 citations verified
Evidence check: judge quoted an external source that does not say that (https://curl.se/libcurl/c/curl_easy_pause.html)
Checked against curl master on 2026-05-19 (7541ae569d82). Dev evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.
Inspect the withdrawn draft reply
This draft belongs to the withdrawn judgment. It may contain unsupported claims.
Thank you for the detailed report and reproducer. The UAF in mev_sh_entry_update is confirmed: the entry pointer is held across the socket_cb call, curl_easy_pause() can re-enter mev_assess() which may free the entry via mev_pollset_diff -> mev_forget_socket -> mev_sh_entry_kill, and lines 275/280 then write to freed memory. The fix of re-fetching the entry from the hash after the callback returns is correct. This is a valid security vulnerability (heap-use-after-free reachable via documented API use from a callback).
Inspect all claims and source code 5 claims · 10 code excerpts
This model judgment was withdrawn. The claim assessments below are preserved for inspection and may be wrong.
Report claims
entry->announced = TRUE; entry->action = (unsigned int)comboaction; are performed on a freed mev_sh_entry pointer after curl_easy_pause() may have freed it
Read the reasoning
The code confirms the UAF path: mev_sh_entry_update fires the socket callback (line 272-273), which can call curl_easy_pause() -> Curl_multi_ev_assess_xfer() -> mev_assess() -> mev_pollset_diff() -> mev_forget_socket() -> mev_sh_entry_kill() -> free(entry). After the callback returns, lines 275 and 280 write to the now-freed entry pointer. mev_assess() does not check multi->in_callback.
lines 275 and 280 in lib/multi_ev.c
Read the reasoning
Lines 275 and 280 in lib/multi_ev.c are the exact writes identified in the report.
calling curl_easy_pause() from within a CURLMOPT_SOCKETFUNCTION callback
Read the reasoning
curl_easy_pause() explicitly checks for and handles being called from within a callback (line 1146), and the CURLMOPT_SOCKETFUNCTION callback is a documented callback entry point. The deterministic checks confirm both symbols exist in curl.
heap-use-after-free causing potential memory corruption or crash
Read the reasoning
Heap-use-after-free is confirmed by the code path; writes to freed memory can cause memory corruption or crash.
use the provided self-contained reproducer that spins up an HTTP server and calls curl_easy_pause() in the socket callback
Read the reasoning
The provided reproducer demonstrates the exact trigger condition: calling curl_easy_pause() from the socket callback when the socket transitions to POLL_IN, causing the poll set to empty and the entry to be freed.
Deterministic checks that failed
- quote 3 of 6 quoted lines appear nowhere in curl, e.g. 'multi->socket_userp, entry->user_data); /* fires app callbac'
What the report says about other projects
confirmed curl_easy_pause(3) can be used from within callbacks
“Unlike most other libcurl functions, curl_easy_pause(3) can be used from within callbacks.” source
not addressed CURLMOPT_SOCKETFUNCTION documentation adds no restriction on calling curl_easy_pause()
Read the original report (excerpt)
Source code
| 1133 | CURLcode curl_easy_pause(CURL *curl, int action) |
| 1134 | { |
| 1135 | CURLcode result = CURLE_OK; |
| 1136 | bool recursive = FALSE; |
| 1137 | bool changed = FALSE; |
| 1138 | struct Curl_easy *data = curl; |
| 1139 | bool recv_paused, recv_paused_new; |
| 1140 | bool send_paused, send_paused_new; |
| 1141 | |
| 1142 | if(!GOOD_EASY_HANDLE(data) || !data->conn) |
| 1143 | /* crazy input, do not continue */ |
| 1144 | return CURLE_BAD_FUNCTION_ARGUMENT; |
| 1145 | |
| 1146 | if(Curl_is_in_callback(data)) |
| C3: curl_easy_pause checks Curl_is_in_callback(data) and handles recursive calls | |
| 1147 | recursive = TRUE; |
| 1148 | |
| 1149 | recv_paused = Curl_xfer_recv_is_paused(data); |
| 1150 | recv_paused_new = (action & CURLPAUSE_RECV); |
| 1151 | send_paused = Curl_xfer_send_is_paused(data); |
| 1152 | send_paused_new = (action & CURLPAUSE_SEND); |
| 1153 | |
| 1154 | if((send_paused != send_paused_new) || |
| 1155 | (send_paused_new != Curl_creader_is_paused(data))) { |
| 1156 | changed = TRUE; |
| 1157 | result = Curl_1st_fatal( |
| 1158 | result, Curl_xfer_pause_send(data, send_paused_new)); |
| 1159 | } |
| 1160 | |
| 1161 | if(recv_paused != recv_paused_new) { |
| 1162 | changed = TRUE; |
| 1163 | result = Curl_1st_fatal( |
| 1164 | result, Curl_xfer_pause_recv(data, recv_paused_new)); |
| 1165 | } |
| 1166 | |
| 1167 | /* If not completely pausing both directions now, run again in any case. */ |
| 1168 | if(!Curl_xfer_is_blocked(data)) { |
| 1169 | /* reset the too-slow time keeper */ |
| 1170 | data->state.keeps_speed.tv_sec = 0; |
| 1171 | if(data->multi) { |
| 1172 | Curl_multi_mark_dirty(data); /* make it run */ |
| 1173 | /* On changes, tell application to update its timers. */ |
| 1174 | if(changed) { |
| 1175 | if(Curl_update_timer(data->multi) && !result) |
| 1176 | result = CURLE_ABORTED_BY_CALLBACK; |
| 1177 | } |
| 1178 | } |
| 1179 | } |
| 1180 | |
| 1181 | if(!result && changed && !data->state.done && data->multi) |
| 1182 | /* pause/unpausing may result in multi event changes */ |
| 1183 | if(Curl_multi_ev_assess_xfer(data->multi, data) && !result) |
| C3: curl_easy_pause calls Curl_multi_ev_assess_xfer when not completely paused | |
| 1184 | result = CURLE_ABORTED_BY_CALLBACK; |
| 1185 | |
| 1186 | if(recursive) |
| 1187 | /* this might have called a callback recursively which might have set this |
| 1188 | to false again on exit */ |
| 1189 | Curl_set_in_callback(data, TRUE); |
| 1190 | |
| 1191 | return result; |
| 1192 | } |
| 1193 | |
| 1194 | static CURLcode easy_connection(struct Curl_easy *data, |
| 1195 | struct connectdata **connp) |
| 1196 | { |
| 1197 | curl_socket_t sfd; |
| 1198 | |
| 1199 | if(!data) |
| 1200 | return CURLE_BAD_FUNCTION_ARGUMENT; |
| 1201 | |
| 1202 | /* only allow these to be called on handles with CURLOPT_CONNECT_ONLY */ |
| 1203 | if(!data->set.connect_only) { |
| 1204 | failf(data, "CONNECT_ONLY is required"); |
| 1205 | return CURLE_UNSUPPORTED_PROTOCOL; |
| 1206 | } |
| 1207 | |
| 1208 | sfd = Curl_getconnectinfo(data, connp); |
| 520 | CURLMcode Curl_multi_ev_assess_xfer(struct Curl_multi *multi, |
| 521 | struct Curl_easy *data) |
| 522 | { |
| 523 | return mev_assess(multi, data, NULL); |
| C3: Curl_multi_ev_assess_xfer calls mev_assess | |
| 524 | } |
| 477 | static CURLMcode mev_assess(struct Curl_multi *multi, |
| 478 | struct Curl_easy *data, |
| 479 | struct connectdata *conn) |
| 480 | { |
| 481 | struct easy_pollset ps, *last_ps; |
| 482 | CURLMcode mresult = CURLM_OK; |
| 483 | |
| 484 | if(!multi || !multi->socket_cb) |
| 485 | return CURLM_OK; |
| 486 | |
| 487 | Curl_pollset_init(&ps); |
| 488 | if(conn) { |
| 489 | CURLcode result = Curl_conn_adjust_pollset(data, conn, &ps); |
| 490 | if(result) { |
| 491 | mresult = (result == CURLE_OUT_OF_MEMORY) ? |
| 492 | CURLM_OUT_OF_MEMORY : CURLM_INTERNAL_ERROR; |
| 493 | goto out; |
| 494 | } |
| 495 | } |
| 496 | else |
| 497 | Curl_multi_pollset(data, &ps); |
| 498 | last_ps = mev_get_last_pollset(data, conn); |
| 499 | |
| 500 | if(!last_ps && ps.n) { |
| 501 | if(conn) |
| 502 | last_ps = mev_add_new_conn_pollset(conn); |
| 503 | else |
| 504 | last_ps = mev_add_new_xfer_pollset(data); |
| 505 | if(!last_ps) { |
| 506 | mresult = CURLM_OUT_OF_MEMORY; |
| 507 | goto out; |
| 508 | } |
| 509 | } |
| 510 | |
| 511 | if(last_ps) |
| 512 | mresult = mev_pollset_diff(multi, data, conn, &ps, last_ps); |
| C1: mev_assess calls mev_pollset_diff which can free the entry via mev_forget_socket -> mev_sh_entry_kill | |
| 513 | else |
| 514 | DEBUGASSERT(!ps.n); |
| 515 | out: |
| 516 | Curl_pollset_cleanup(&ps); |
| 517 | return mresult; |
| 518 | } |
| 185 | static CURLMcode mev_forget_socket(struct Curl_multi *multi, |
| 186 | struct Curl_easy *data, |
| 187 | curl_socket_t s, |
| 188 | const char *cause) |
| 189 | { |
| 190 | struct mev_sh_entry *entry = mev_sh_entry_get(&multi->ev.sh_entries, s); |
| 191 | int rc = 0; |
| 192 | |
| 193 | if(!entry) /* we never knew or already forgot about this socket */ |
| 194 | return CURLM_OK; |
| 195 | |
| 196 | /* We managed this socket before, tell the socket callback to forget it. */ |
| 197 | if(entry->announced && multi->socket_cb) { |
| 198 | NOVERBOSE((void)cause); |
| 199 | CURL_TRC_M(data, "ev %s, call(fd=%" FMT_SOCKET_T ", ev=REMOVE)", cause, s); |
| 200 | mev_in_callback(multi, TRUE); |
| 201 | rc = multi->socket_cb(data, s, CURL_POLL_REMOVE, |
| 202 | multi->socket_userp, entry->user_data); |
| 203 | mev_in_callback(multi, FALSE); |
| 204 | entry->announced = FALSE; |
| 205 | } |
| 206 | |
| 207 | mev_sh_entry_kill(multi, s); |
| C1: mev_forget_socket calls mev_sh_entry_kill which frees the entry | |
| 208 | if(rc == -1) { |
| 209 | multi->dead = TRUE; |
| 210 | return CURLM_ABORTED_BY_CALLBACK; |
| 211 | } |
| 212 | return CURLM_OK; |
| 213 | } |
| 284 | static CURLMcode mev_pollset_diff(struct Curl_multi *multi, |
| 285 | struct Curl_easy *data, |
| 286 | struct connectdata *conn, |
| 287 | struct easy_pollset *ps, |
| 288 | struct easy_pollset *prev_ps) |
| 289 | { |
| 290 | struct mev_sh_entry *entry; |
| 291 | curl_socket_t s; |
| 292 | unsigned int i, j; |
| 293 | CURLMcode mresult; |
| 294 | |
| 295 | /* The transfer `data` reports in `ps` the sockets it is interested |
| 296 | * in and which combination of CURL_POLL_IN/CURL_POLL_OUT it wants |
| 297 | * to have monitored for events. |
| 298 | * There can be more than 1 transfer interested in the same socket |
| 299 | * and 1 transfer might be interested in more than 1 socket. |
| 300 | * `prev_ps` is the pollset copy from the previous call here. On |
| 301 | * the 1st call it will be empty. |
| 302 | */ |
| 303 | DEBUGASSERT(ps); |
| 304 | DEBUGASSERT(prev_ps); |
| 305 | |
| 306 | /* Handle changes to sockets the transfer is interested in. */ |
| 307 | for(i = 0; i < ps->n; i++) { |
| 308 | unsigned char last_action; |
| 309 | bool first_time = FALSE; /* data/conn appears first time on socket */ |
| 310 | |
| 311 | s = ps->sockets[i]; |
| 312 | /* Have we handled this socket before? */ |
| 313 | entry = mev_sh_entry_get(&multi->ev.sh_entries, s); |
| 314 | if(!entry) { |
| 315 | /* new socket, add new entry */ |
| 316 | first_time = TRUE; |
| 317 | entry = mev_sh_entry_add(&multi->ev.sh_entries, s); |
| 318 | if(!entry) /* fatal */ |
| 319 | return CURLM_OUT_OF_MEMORY; |
| 320 | CURL_TRC_M(data, "ev new entry fd=%" FMT_SOCKET_T, s); |
| 321 | } |
| 322 | else if(conn) { |
| 323 | first_time = !mev_sh_entry_conn_known(entry, conn); |
| 324 | } |
| 325 | else { |
| 326 | first_time = !mev_sh_entry_xfer_known(entry, data); |
| 327 | } |
| 328 | |
| 329 | /* What was the previous action the transfer had regarding this socket? |
| 330 | * If the transfer is new to the socket, disregard the information |
| 331 | * in `last_poll`, because the socket might have been destroyed and |
| 332 | * reopened. We would have cleared the sh_entry for that, but the socket |
| 333 | * might still be mentioned in the hashed pollsets. */ |
| 334 | last_action = 0; |
| 335 | if(first_time) { |
| 336 | if(conn) { |
| 337 | if(!mev_sh_entry_conn_add(entry, conn)) |
| 338 | return CURLM_OUT_OF_MEMORY; |
| 339 | } |
| 340 | else { |
| 341 | if(!mev_sh_entry_xfer_add(entry, data)) |
| 342 | return CURLM_OUT_OF_MEMORY; |
| 343 | } |
| 344 | CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", added %s #%" FMT_OFF_T |
| 345 | ", total=%u/%d (xfer/conn)", s, |
| 346 | conn ? "connection" : "transfer", |
| 347 | conn ? conn->connection_id : data->mid, |
| 348 | Curl_uint32_spbset_count(&entry->xfers), |
| 349 | entry->conn ? 1 : 0); |
| 350 | } |
| 351 | else { |
| 352 | for(j = 0; j < prev_ps->n; j++) { |
| 353 | if(s == prev_ps->sockets[j]) { |
| 354 | last_action = prev_ps->actions[j]; |
| 355 | break; |
| 356 | } |
| 357 | } |
| 358 | } |
| 359 | /* track readers/writers changes and report to socket callback */ |
| 360 | mresult = mev_sh_entry_update(multi, data, entry, s, |
| 361 | last_action, ps->actions[i]); |
| 362 | if(mresult) |
| 363 | return mresult; |
| 364 | } |
| 365 | |
| 366 | /* Handle changes to sockets the transfer is NO LONGER interested in. */ |
| 367 | for(i = 0; i < prev_ps->n; i++) { |
| 368 | bool stillused = FALSE; |
| 369 | |
| 370 | s = prev_ps->sockets[i]; |
| 371 | for(j = 0; j < ps->n; j++) { |
| 372 | if(s == ps->sockets[j]) { |
| 373 | /* socket is still supervised */ |
| 374 | stillused = TRUE; |
| 375 | break; |
| 376 | } |
| 377 | } |
| 378 | if(stillused) |
| 379 | continue; |
| 380 | |
| 381 | entry = mev_sh_entry_get(&multi->ev.sh_entries, s); |
| 382 | /* if entry does not exist, we were either never told about it or |
| 383 | * have already cleaned up this socket via Curl_multi_ev_socket_done(). |
| 384 | * In other words: this is perfectly normal */ |
| 385 | if(!entry) |
| 386 | continue; |
| 387 | |
| 388 | if(conn && !mev_sh_entry_conn_remove(entry, conn)) { |
| 389 | /* `conn` says in `prev_ps` that it had been using a socket, |
| 390 | * but `conn` has not been registered for it. |
| 391 | * This should not happen if our book-keeping is correct? */ |
| 392 | CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", conn lost " |
| 393 | "interest but is not registered", s); |
| 394 | DEBUGASSERT(NULL); |
| 395 | continue; |
| 396 | } |
| 397 | |
| 398 | if(!conn && !mev_sh_entry_xfer_remove(entry, data)) { |
| 399 | /* `data` says in `prev_ps` that it had been using a socket, |
| 400 | * but `data` has not been registered for it. |
| 401 | * This should not happen if our book-keeping is correct? */ |
| 402 | CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", transfer lost " |
| 403 | "interest but is not registered", s); |
| 404 | DEBUGASSERT(NULL); |
| 405 | continue; |
| 406 | } |
| 407 | |
| 408 | if(mev_sh_entry_user_count(entry)) { |
| 409 | /* track readers/writers changes and report to socket callback */ |
| 410 | mresult = mev_sh_entry_update(multi, data, entry, s, |
| 411 | prev_ps->actions[i], 0); |
| 412 | if(mresult) |
| 413 | return mresult; |
| 414 | CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", removed transfer, " |
| 415 | "total=%u/%d (xfer/conn)", s, |
| 416 | Curl_uint32_spbset_count(&entry->xfers), |
| 417 | entry->conn ? 1 : 0); |
| 418 | } |
| 419 | else { |
| 420 | mresult = mev_forget_socket(multi, data, s, "last user gone"); |
| C1: mev_pollset_diff calls mev_forget_socket when entry has no users | |
| 421 | if(mresult) |
| 422 | return mresult; |
| 423 | } |
| 424 | } /* for loop over num */ |
| 425 | |
| 426 | /* Remember for next time */ |
| 427 | Curl_pollset_move(prev_ps, ps); |
| 428 | return CURLM_OK; |
| 429 | } |
| 121 | static void mev_sh_entry_kill(struct Curl_multi *multi, curl_socket_t s) |
| 122 | { |
| 123 | Curl_hash_delete(&multi->ev.sh_entries, (char *)&s, sizeof(curl_socket_t)); |
| C1: mev_sh_entry_kill deletes entry from hash, freeing it | |
| 124 | } |
| 215 | static CURLMcode mev_sh_entry_update(struct Curl_multi *multi, |
| 216 | struct Curl_easy *data, |
| 217 | struct mev_sh_entry *entry, |
| 218 | curl_socket_t s, |
| 219 | unsigned char last_action, |
| 220 | unsigned char cur_action) |
| 221 | { |
| 222 | int rc, comboaction; |
| 223 | |
| 224 | /* we should only be called when the callback exists */ |
| 225 | DEBUGASSERT(multi->socket_cb); |
| 226 | if(!multi->socket_cb) |
| 227 | return CURLM_OK; |
| 228 | |
| 229 | /* Transfer `data` goes from `last_action` to `cur_action` on socket `s` |
| 230 | * with `multi->ev.sh_entries` entry `entry`. Update `entry` and trigger |
| 231 | * `multi->socket_cb` on change, if the callback is set. */ |
| 232 | if(last_action == cur_action) /* nothing from `data` changed */ |
| 233 | return CURLM_OK; |
| 234 | |
| 235 | if(last_action & CURL_POLL_IN) { |
| 236 | DEBUGASSERT(entry->readers); |
| 237 | if(!(cur_action & CURL_POLL_IN)) |
| 238 | entry->readers--; |
| 239 | } |
| 240 | else if(cur_action & CURL_POLL_IN) |
| 241 | entry->readers++; |
| 242 | |
| 243 | if(last_action & CURL_POLL_OUT) { |
| 244 | DEBUGASSERT(entry->writers); |
| 245 | if(!(cur_action & CURL_POLL_OUT)) |
| 246 | entry->writers--; |
| 247 | } |
| 248 | else if(cur_action & CURL_POLL_OUT) |
| 249 | entry->writers++; |
| 250 | |
| 251 | DEBUGASSERT(entry->readers <= mev_sh_entry_user_count(entry)); |
| 252 | DEBUGASSERT(entry->writers <= mev_sh_entry_user_count(entry)); |
| 253 | DEBUGASSERT(entry->writers + entry->readers); |
| 254 | |
| 255 | CURL_TRC_M(data, "ev update fd=%" FMT_SOCKET_T ", action '%s%s' -> '%s%s'" |
| 256 | " (%u/%u r/w)", s, |
| 257 | (last_action & CURL_POLL_IN) ? "IN" : "", |
| 258 | (last_action & CURL_POLL_OUT) ? "OUT" : "", |
| 259 | (cur_action & CURL_POLL_IN) ? "IN" : "", |
| 260 | (cur_action & CURL_POLL_OUT) ? "OUT" : "", |
| 261 | entry->readers, entry->writers); |
| 262 | |
| 263 | comboaction = (entry->writers ? CURL_POLL_OUT : 0) | |
| 264 | (entry->readers ? CURL_POLL_IN : 0); |
| 265 | if(((int)entry->action == comboaction)) /* nothing for socket changed */ |
| 266 | return CURLM_OK; |
| 267 | |
| 268 | CURL_TRC_M(data, "ev update call(fd=%" FMT_SOCKET_T ", ev=%s%s)", |
| 269 | s, (comboaction & CURL_POLL_IN) ? "IN" : "", |
| 270 | (comboaction & CURL_POLL_OUT) ? "OUT" : ""); |
| 271 | mev_in_callback(multi, TRUE); |
| 272 | rc = multi->socket_cb(data, s, comboaction, multi->socket_userp, |
| C1: mev_sh_entry_update calls socket_cb while holding entry pointer | |
| 273 | entry->user_data); |
| 274 | mev_in_callback(multi, FALSE); |
| 275 | entry->announced = TRUE; |
| C1: entry->announced = TRUE; writes to entry after callback returns | |
| C2: entry->announced = TRUE; | |
| C4: Write to freed memory (entry->announced) | |
| 276 | if(rc == -1) { |
| 277 | multi->dead = TRUE; |
| 278 | return CURLM_ABORTED_BY_CALLBACK; |
| 279 | } |
| 280 | entry->action = (unsigned int)comboaction; |
| C1: entry->action = (unsigned int)comboaction; second write to entry | |
| C2: entry->action = (unsigned int)comboaction; | |
| C4: Write to freed memory (entry->action) | |
| 281 | return CURLM_OK; |
| 282 | } |
Show 42 lines (not cited by the judge)
| 477 | static CURLMcode mev_assess(struct Curl_multi *multi, |
| 478 | struct Curl_easy *data, |
| 479 | struct connectdata *conn) |
| 480 | { |
| 481 | struct easy_pollset ps, *last_ps; |
| 482 | CURLMcode mresult = CURLM_OK; |
| 483 | |
| 484 | if(!multi || !multi->socket_cb) |
| 485 | return CURLM_OK; |
| 486 | |
| 487 | Curl_pollset_init(&ps); |
| 488 | if(conn) { |
| 489 | CURLcode r = Curl_conn_adjust_pollset(data, conn, &ps); |
| 490 | if(r) { |
| 491 | mresult = (r == CURLE_OUT_OF_MEMORY) ? |
| 492 | CURLM_OUT_OF_MEMORY : CURLM_INTERNAL_ERROR; |
| 493 | goto out; |
| 494 | } |
| 495 | } |
| 496 | else |
| 497 | Curl_multi_pollset(data, &ps); |
| 498 | last_ps = mev_get_last_pollset(data, conn); |
| 499 | |
| 500 | if(!last_ps && ps.n) { |
| 501 | if(conn) |
| 502 | last_ps = mev_add_new_conn_pollset(conn); |
| 503 | else |
| 504 | last_ps = mev_add_new_xfer_pollset(data); |
| 505 | if(!last_ps) { |
| 506 | mresult = CURLM_OUT_OF_MEMORY; |
| 507 | goto out; |
| 508 | } |
| 509 | } |
| 510 | |
| 511 | if(last_ps) |
| 512 | mresult = mev_pollset_diff(multi, data, conn, &ps, last_ps); |
| 513 | else |
| 514 | DEBUGASSERT(!ps.n); |
| 515 | out: |
| 516 | Curl_pollset_cleanup(&ps); |
| 517 | return mresult; |
| 518 | } |
Show 4 lines (not cited by the judge)
| 38 | static void mev_in_callback(struct Curl_multi *multi, bool value) |
| 39 | { |
| 40 | multi->in_callback = value; |
| 41 | } |
Show 9 lines (not cited by the judge)
| 82 | static struct mev_sh_entry *mev_sh_entry_get(struct Curl_hash *sh, |
| 83 | curl_socket_t s) |
| 84 | { |
| 85 | if(s != CURL_SOCKET_BAD) { |
| 86 | /* only look for proper sockets */ |
| 87 | return Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t)); |
| 88 | } |
| 89 | return NULL; |
| 90 | } |