Skip to content

Commit 9d25daa

Browse files
Taylor Brandstettermibrunin
authored andcommitted
[Backport] Security bug 1184441
Manual backport of patch originally reviewed on https://webrtc-review.googlesource.com/c/src/+/215060: [Merge M86] - Reland "Fix race between destroying SctpTransport and receiving notification on timer thread." This reverts commit 8a38b1cf681cd77f0d59a68fb45d8dedbd7d4cee. Reason for reland: Problem was identified; has something to do with the unique_ptr with the custom deleter. Original change's description: > Revert "Fix race between destroying SctpTransport and receiving notification on timer thread." > > This reverts commit a88fe7be146b9b85575504d4d5193c007f2e3de4. > > Reason for revert: Breaks downstream test, still investigating. > > Original change's description: > > Fix race between destroying SctpTransport and receiving notification on timer thread. > > > > This gets rid of the SctpTransportMap::Retrieve method and forces > > everything to go through PostToTransportThread, which behaves safely > > with relation to the transport's destruction. > > > > Bug: webrtc:12467 > > Change-Id: Id4a723c2c985be2a368d2cc5c5e62deb04c509ab > > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/208800 > > Reviewed-by: Niels Moller <nisse@webrtc.org> > > Commit-Queue: Taylor <deadbeef@webrtc.org> > > Cr-Commit-Position: refs/heads/master@{#33364} > > TBR=nisse@webrtc.org > > Bug: webrtc:12467 > Change-Id: Ib5d815a2cbca4feb25f360bff7ed62c02d1910a0 > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/209820 > Reviewed-by: Taylor <deadbeef@webrtc.org> > Commit-Queue: Taylor <deadbeef@webrtc.org> > Cr-Commit-Position: refs/heads/master@{#33386} TBR=nisse@webrtc.org Bug: webrtc:12467 Change-Id: I5f9fcd6df7a211e6edfa64577fc953833f4d9b79 Reviewed-by: Niels Moller <nisse@webrtc.org> Reviewed-by: Florent Castelli <orphis@webrtc.org> Commit-Queue: Taylor <deadbeef@webrtc.org> Cr-Original-Commit-Position: refs/heads/master@{#33427} No-Try: True No-Presubmit: True Reviewed-by: Taylor <deadbeef@webrtc.org> Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org> Cr-Commit-Position: refs/branch-heads/4240@{#19} Cr-Branched-From: 93a9d19d4eb53b3f4fb4d22e6c54f2e2824437eb-refs/heads/master@{#31969} Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
1 parent a234ab0 commit 9d25daa

File tree

2 files changed

+111
-86
lines changed

2 files changed

+111
-86
lines changed

chromium/third_party/webrtc/media/sctp/sctp_transport.cc

Lines changed: 102 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ enum PreservedErrno {
2020
// Successful return value from usrsctp callbacks. Is not actually used by
2121
// usrsctp, but all example programs for usrsctp use 1 as their return value.
2222
constexpr int kSctpSuccessReturn = 1;
23+
constexpr int kSctpErrorReturn = 0;
2324

2425
} // namespace
2526

@@ -83,6 +84,19 @@ enum {
8384
// Should only be modified by UsrSctpWrapper.
8485
ABSL_CONST_INIT cricket::SctpTransportMap* g_transport_map_ = nullptr;
8586

87+
// Helper that will call C's free automatically.
88+
// TODO(b/181900299): Figure out why unique_ptr with a custom deleter is causing
89+
// issues in a certain build environment.
90+
class AutoFreedPointer {
91+
public:
92+
explicit AutoFreedPointer(void* ptr) : ptr_(ptr) {}
93+
AutoFreedPointer(AutoFreedPointer&& o) : ptr_(o.ptr_) { o.ptr_ = nullptr; }
94+
~AutoFreedPointer() { free(ptr_); }
95+
void* get() const { return ptr_; }
96+
private:
97+
void* ptr_;
98+
};
99+
86100
// Helper for logging SCTP messages.
87101
#if defined(__GNUC__)
88102
__attribute__((__format__(__printf__, 1, 2)))
@@ -239,32 +253,20 @@ class SctpTransportMap {
239253
return map_.erase(id) > 0;
240254
}
241255

242-
// Must be called on the transport's network thread to protect against
243-
// simultaneous deletion/deregistration of the transport; if that's not
244-
// guaranteed, use ExecuteWithLock.
245-
SctpTransport* Retrieve(uintptr_t id) const {
246-
webrtc::MutexLock lock(&lock_);
247-
SctpTransport* transport = RetrieveWhileHoldingLock(id);
248-
if (transport) {
249-
RTC_DCHECK_RUN_ON(transport->network_thread());
250-
}
251-
return transport;
252-
}
253-
254256
// Posts |action| to the network thread of the transport identified by |id|
255257
// and returns true if found, all while holding a lock to protect against the
256258
// transport being simultaneously deleted/deregistered, or returns false if
257259
// not found.
258-
bool PostToTransportThread(uintptr_t id,
259-
std::function<void(SctpTransport*)> action) const {
260+
template <typename F>
261+
bool PostToTransportThread(uintptr_t id, F action) const {
260262
webrtc::MutexLock lock(&lock_);
261263
SctpTransport* transport = RetrieveWhileHoldingLock(id);
262264
if (!transport) {
263265
return false;
264266
}
265267
transport->invoker_.AsyncInvoke<void>(
266-
RTC_FROM_HERE, transport->network_thread_, [transport, action]() {
267-
action(transport); });
268+
RTC_FROM_HERE, transport->network_thread_,
269+
[transport, action{std::move(action)}]() { action(transport); });
268270
return true;
269271
}
270272

@@ -406,7 +408,7 @@ class SctpTransport::UsrSctpWrapper {
406408
if (!found) {
407409
RTC_LOG(LS_ERROR)
408410
<< "OnSctpOutboundPacket: Failed to get transport for socket ID "
409-
<< addr;
411+
<< addr << "; possibly was already destroyed.";
410412
return EINVAL;
411413
}
412414
return 0;
@@ -423,28 +425,45 @@ class SctpTransport::UsrSctpWrapper {
423425
struct sctp_rcvinfo rcv,
424426
int flags,
425427
void* ulp_info) {
426-
SctpTransport* transport = GetTransportFromSocket(sock);
427-
if (!transport) {
428+
AutoFreedPointer owned_data(data);
429+
absl::optional<uintptr_t> id = GetTransportIdFromSocket(sock);
430+
if (!id) {
428431
RTC_LOG(LS_ERROR)
429-
<< "OnSctpInboundPacket: Failed to get transport for socket " << sock
430-
<< "; possibly was already destroyed.";
431-
free(data);
432-
return 0;
432+
<< "OnSctpInboundPacket: Failed to get transport ID from socket "
433+
<< sock;
434+
return kSctpErrorReturn;
435+
}
436+
437+
if (!g_transport_map_) {
438+
RTC_LOG(LS_ERROR)
439+
<< "OnSctpInboundPacket called after usrsctp uninitialized?";
440+
return kSctpErrorReturn;
441+
}
442+
// PostsToTransportThread protects against the transport being
443+
// simultaneously deregistered/deleted, since this callback may come from
444+
// the SCTP timer thread and thus race with the network thread.
445+
bool found = g_transport_map_->PostToTransportThread(
446+
*id, [owned_data{std::move(owned_data)}, length, rcv,
447+
flags](SctpTransport* transport) {
448+
transport->OnDataOrNotificationFromSctp(owned_data.get(), length, rcv,
449+
flags);
450+
});
451+
if (!found) {
452+
RTC_LOG(LS_ERROR)
453+
<< "OnSctpInboundPacket: Failed to get transport for socket ID "
454+
<< *id << "; possibly was already destroyed.";
455+
return kSctpErrorReturn;
433456
}
434-
// Sanity check that both methods of getting the SctpTransport pointer
435-
// yield the same result.
436-
RTC_CHECK_EQ(transport, static_cast<SctpTransport*>(ulp_info));
437-
int result =
438-
transport->OnDataOrNotificationFromSctp(data, length, rcv, flags);
439-
free(data);
440-
return result;
457+
return kSctpSuccessReturn;
441458
}
442459

443-
static SctpTransport* GetTransportFromSocket(struct socket* sock) {
460+
static absl::optional<uintptr_t> GetTransportIdFromSocket(
461+
struct socket* sock) {
462+
absl::optional<uintptr_t> ret;
444463
struct sockaddr* addrs = nullptr;
445464
int naddrs = usrsctp_getladdrs(sock, 0, &addrs);
446465
if (naddrs <= 0 || addrs[0].sa_family != AF_CONN) {
447-
return nullptr;
466+
return ret;
448467
}
449468
// usrsctp_getladdrs() returns the addresses bound to this socket, which
450469
// contains the SctpTransport id as sconn_addr. Read the id,
@@ -453,17 +472,10 @@ class SctpTransport::UsrSctpWrapper {
453472
// id of the transport that created them, so [0] is as good as any other.
454473
struct sockaddr_conn* sconn =
455474
reinterpret_cast<struct sockaddr_conn*>(&addrs[0]);
456-
if (!g_transport_map_) {
457-
RTC_LOG(LS_ERROR)
458-
<< "GetTransportFromSocket called after usrsctp uninitialized?";
459-
usrsctp_freeladdrs(addrs);
460-
return nullptr;
461-
}
462-
SctpTransport* transport = g_transport_map_->Retrieve(
463-
reinterpret_cast<uintptr_t>(sconn->sconn_addr));
475+
ret = reinterpret_cast<uintptr_t>(sconn->sconn_addr);
464476
usrsctp_freeladdrs(addrs);
465477

466-
return transport;
478+
return ret;
467479
}
468480

469481
// TODO(crbug.com/webrtc/11899): This is a legacy callback signature, remove
@@ -472,14 +484,26 @@ class SctpTransport::UsrSctpWrapper {
472484
// Fired on our I/O thread. SctpTransport::OnPacketReceived() gets
473485
// a packet containing acknowledgments, which goes into usrsctp_conninput,
474486
// and then back here.
475-
SctpTransport* transport = GetTransportFromSocket(sock);
476-
if (!transport) {
487+
absl::optional<uintptr_t> id = GetTransportIdFromSocket(sock);
488+
if (!id) {
477489
RTC_LOG(LS_ERROR)
478-
<< "SendThresholdCallback: Failed to get transport for socket "
479-
<< sock << "; possibly was already destroyed.";
490+
<< "SendThresholdCallback: Failed to get transport ID from socket "
491+
<< sock;
480492
return 0;
481493
}
482-
transport->OnSendThresholdCallback();
494+
if (!g_transport_map_) {
495+
RTC_LOG(LS_ERROR)
496+
<< "SendThresholdCallback called after usrsctp uninitialized?";
497+
return 0;
498+
}
499+
bool found = g_transport_map_->PostToTransportThread(
500+
*id,
501+
[](SctpTransport* transport) { transport->OnSendThresholdCallback(); });
502+
if (!found) {
503+
RTC_LOG(LS_ERROR)
504+
<< "SendThresholdCallback: Failed to get transport for socket ID "
505+
<< *id << "; possibly was already destroyed.";
506+
}
483507
return 0;
484508
}
485509

@@ -489,17 +513,26 @@ class SctpTransport::UsrSctpWrapper {
489513
// Fired on our I/O thread. SctpTransport::OnPacketReceived() gets
490514
// a packet containing acknowledgments, which goes into usrsctp_conninput,
491515
// and then back here.
492-
SctpTransport* transport = GetTransportFromSocket(sock);
493-
if (!transport) {
516+
absl::optional<uintptr_t> id = GetTransportIdFromSocket(sock);
517+
if (!id) {
494518
RTC_LOG(LS_ERROR)
495-
<< "SendThresholdCallback: Failed to get transport for socket "
496-
<< sock << "; possibly was already destroyed.";
519+
<< "SendThresholdCallback: Failed to get transport ID from socket "
520+
<< sock;
497521
return 0;
498522
}
499-
// Sanity check that both methods of getting the SctpTransport pointer
500-
// yield the same result.
501-
RTC_CHECK_EQ(transport, static_cast<SctpTransport*>(ulp_info));
502-
transport->OnSendThresholdCallback();
523+
if (!g_transport_map_) {
524+
RTC_LOG(LS_ERROR)
525+
<< "SendThresholdCallback called after usrsctp uninitialized?";
526+
return 0;
527+
}
528+
bool found = g_transport_map_->PostToTransportThread(
529+
*id,
530+
[](SctpTransport* transport) { transport->OnSendThresholdCallback(); });
531+
if (!found) {
532+
RTC_LOG(LS_ERROR)
533+
<< "SendThresholdCallback: Failed to get transport for socket ID "
534+
<< *id << "; possibly was already destroyed.";
535+
}
503536
return 0;
504537
}
505538
};
@@ -1150,24 +1183,25 @@ void SctpTransport::OnPacketFromSctpToNetwork(
11501183
rtc::PacketOptions(), PF_NORMAL);
11511184
}
11521185

1153-
int SctpTransport::InjectDataOrNotificationFromSctpForTesting(
1186+
void SctpTransport::InjectDataOrNotificationFromSctpForTesting(
11541187
void* data,
11551188
size_t length,
11561189
struct sctp_rcvinfo rcv,
11571190
int flags) {
1158-
return OnDataOrNotificationFromSctp(data, length, rcv, flags);
1191+
OnDataOrNotificationFromSctp(data, length, rcv, flags);
11591192
}
11601193

1161-
int SctpTransport::OnDataOrNotificationFromSctp(void* data,
1162-
size_t length,
1163-
struct sctp_rcvinfo rcv,
1164-
int flags) {
1194+
void SctpTransport::OnDataOrNotificationFromSctp(void* data,
1195+
size_t length,
1196+
struct sctp_rcvinfo rcv,
1197+
int flags) {
1198+
RTC_DCHECK_RUN_ON(network_thread_);
11651199
// If data is NULL, the SCTP association has been closed.
11661200
if (!data) {
11671201
RTC_LOG(LS_INFO) << debug_name_
11681202
<< "->OnDataOrNotificationFromSctp(...): "
11691203
"No data; association closed.";
1170-
return kSctpSuccessReturn;
1204+
return;
11711205
}
11721206

11731207
// Handle notifications early.
@@ -1180,13 +1214,10 @@ int SctpTransport::OnDataOrNotificationFromSctp(void* data,
11801214
<< "->OnDataOrNotificationFromSctp(...): SCTP notification"
11811215
<< " length=" << length;
11821216

1183-
// Copy and dispatch asynchronously
11841217
rtc::CopyOnWriteBuffer notification(reinterpret_cast<uint8_t*>(data),
11851218
length);
1186-
invoker_.AsyncInvoke<void>(
1187-
RTC_FROM_HERE, network_thread_,
1188-
rtc::Bind(&SctpTransport::OnNotificationFromSctp, this, notification));
1189-
return kSctpSuccessReturn;
1219+
OnNotificationFromSctp(notification);
1220+
return;
11901221
}
11911222

11921223
// Log data chunk
@@ -1204,7 +1235,7 @@ int SctpTransport::OnDataOrNotificationFromSctp(void* data,
12041235
// Unexpected PPID, dropping
12051236
RTC_LOG(LS_ERROR) << "Received an unknown PPID " << ppid
12061237
<< " on an SCTP packet. Dropping.";
1207-
return kSctpSuccessReturn;
1238+
return;
12081239
}
12091240

12101241
// Expect only continuation messages belonging to the same SID. The SCTP
@@ -1240,7 +1271,7 @@ int SctpTransport::OnDataOrNotificationFromSctp(void* data,
12401271
if (partial_incoming_message_.size() < kSctpSendBufferSize) {
12411272
// We still have space in the buffer. Continue buffering chunks until
12421273
// the message is complete before handing it out.
1243-
return kSctpSuccessReturn;
1274+
return;
12441275
} else {
12451276
// The sender is exceeding the maximum message size that we announced.
12461277
// Spit out a warning but still hand out the partial message. Note that
@@ -1254,17 +1285,11 @@ int SctpTransport::OnDataOrNotificationFromSctp(void* data,
12541285
}
12551286
}
12561287

1257-
// Dispatch the complete message.
1258-
// The ownership of the packet transfers to |invoker_|. Using
1259-
// CopyOnWriteBuffer is the most convenient way to do this.
1260-
invoker_.AsyncInvoke<void>(
1261-
RTC_FROM_HERE, network_thread_,
1262-
rtc::Bind(&SctpTransport::OnDataFromSctpToTransport, this, params,
1263-
partial_incoming_message_));
1288+
// Dispatch the complete message and reset the message buffer.
12641289

1265-
// Reset the message buffer
1290+
// Dispatch the complete message and reset the message buffer.
1291+
OnDataFromSctpToTransport(params, partial_incoming_message_);
12661292
partial_incoming_message_.Clear();
1267-
return kSctpSuccessReturn;
12681293
}
12691294

12701295
void SctpTransport::OnDataFromSctpToTransport(

chromium/third_party/webrtc/media/sctp/sctp_transport.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@ class SctpTransport : public SctpTransportInternal,
9696
void set_debug_name_for_testing(const char* debug_name) override {
9797
debug_name_ = debug_name;
9898
}
99-
int InjectDataOrNotificationFromSctpForTesting(void* data,
100-
size_t length,
101-
struct sctp_rcvinfo rcv,
102-
int flags);
99+
void InjectDataOrNotificationFromSctpForTesting(void* data,
100+
size_t length,
101+
struct sctp_rcvinfo rcv,
102+
int flags);
103103

104104
// Exposed to allow Post call from c-callbacks.
105105
// TODO(deadbeef): Remove this or at least make it return a const pointer.
@@ -180,12 +180,12 @@ class SctpTransport : public SctpTransportInternal,
180180
// Called using |invoker_| to send packet on the network.
181181
void OnPacketFromSctpToNetwork(const rtc::CopyOnWriteBuffer& buffer);
182182

183-
// Called on the SCTP thread.
183+
// Called on the network thread.
184184
// Flags are standard socket API flags (RFC 6458).
185-
int OnDataOrNotificationFromSctp(void* data,
186-
size_t length,
187-
struct sctp_rcvinfo rcv,
188-
int flags);
185+
void OnDataOrNotificationFromSctp(void* data,
186+
size_t length,
187+
struct sctp_rcvinfo rcv,
188+
int flags);
189189
// Called using |invoker_| to decide what to do with the data.
190190
void OnDataFromSctpToTransport(const ReceiveDataParams& params,
191191
const rtc::CopyOnWriteBuffer& buffer);

0 commit comments

Comments
 (0)