Skip to content

Commit 9d1bf8f

Browse files
authored
Merge pull request #41539 from rjmccall/uaf-task-tracing
Fix a use-after-free with task tracing
2 parents 3197292 + c70d73c commit 9d1bf8f

File tree

5 files changed

+59
-20
lines changed

5 files changed

+59
-20
lines changed

stdlib/public/Concurrency/Actor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1540,7 +1540,7 @@ static void swift_job_runImpl(Job *job, ExecutorRef executor) {
15401540
SWIFT_TASK_DEBUG_LOG("%s(%p)", __func__, job);
15411541
runJobInEstablishedExecutorContext(job);
15421542

1543-
concurrency::trace::job_run_end(job, &executor, traceHandle);
1543+
concurrency::trace::job_run_end(&executor, traceHandle);
15441544
trackingInfo.leave();
15451545

15461546
// Give up the current executor if this is a switching context

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,12 @@ class alignas(2 * sizeof(void*)) ActiveTaskStatus {
280280
/// actor. This bit is cleared when a starts running on a thread, suspends
281281
/// or is completed.
282282
IsEnqueued = 0x1000,
283+
284+
#ifndef NDEBUG
285+
/// Task has been completed. This is purely used to enable an assertion
286+
/// that the task is completed when we destroy it.
287+
IsComplete = 0x2000,
288+
#endif
283289
};
284290

285291
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION && SWIFT_POINTER_IS_4_BYTES
@@ -393,6 +399,20 @@ class alignas(2 * sizeof(void*)) ActiveTaskStatus {
393399
#endif
394400
}
395401

402+
#ifndef NDEBUG
403+
bool isComplete() const {
404+
return Flags & IsComplete;
405+
}
406+
407+
ActiveTaskStatus withComplete() const {
408+
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
409+
return ActiveTaskStatus(Record, Flags | IsComplete, ExecutionLock);
410+
#else
411+
return ActiveTaskStatus(Record, Flags | IsComplete);
412+
#endif
413+
}
414+
#endif
415+
396416
/// Is there a lock on the linked list of status records?
397417
bool isStatusRecordLocked() const { return Flags & IsStatusRecordLocked; }
398418
ActiveTaskStatus withLockingRecord(TaskStatusRecord *lockRecord) const {
@@ -564,6 +584,9 @@ struct AsyncTask::PrivateStorage {
564584
auto newStatus = oldStatus.withRunning(false);
565585
newStatus = newStatus.withoutStoredPriorityEscalation();
566586
newStatus = newStatus.withoutEnqueued();
587+
#ifndef NDEBUG
588+
newStatus = newStatus.withComplete();
589+
#endif
567590

568591
// This can fail since the task can still get concurrently cancelled or
569592
// escalated.
@@ -650,6 +673,7 @@ retry:;
650673
while (true) {
651674
// We can get here from being suspended or being enqueued
652675
assert(!oldStatus.isRunning());
676+
assert(!oldStatus.isComplete());
653677

654678
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
655679
// Task's priority is greater than the thread's - do a self escalation

stdlib/public/Concurrency/Tracing.h

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,22 @@ void job_enqueue_global_with_delay(unsigned long long delay, Job *job);
7575

7676
void job_enqueue_main_executor(Job *job);
7777

78-
// This returns a handle that must be passed to the corresponding call to
79-
// task_run_end.
80-
uint64_t job_run_begin(Job *job, ExecutorRef *executor);
81-
82-
void job_run_end(Job *job, ExecutorRef *executor, uint64_t beginHandle);
78+
struct job_run_info {
79+
/// The ID of the task that started running.
80+
uint64_t taskId;
81+
82+
/// The signpost ID for this task execution, or OS_SIGNPOST_ID_INVALID
83+
/// if the job was not a task.
84+
uint64_t handle;
85+
};
86+
87+
// This returns information that must be passed to the corresponding
88+
// call to task_run_end. Any information we want to log must be
89+
// extracted from the job when we start to run it because execution
90+
// will invalidate the job.
91+
job_run_info job_run_begin(Job *job, ExecutorRef *executor);
92+
93+
void job_run_end(ExecutorRef *executor, job_run_info info);
8394

8495
} // namespace trace
8596
} // namespace concurrency

stdlib/public/Concurrency/TracingSignpost.h

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -244,29 +244,33 @@ inline void job_enqueue_main_executor(Job *job) {
244244
}
245245
}
246246

247-
inline uint64_t job_run_begin(Job *job, ExecutorRef *executor) {
247+
inline job_run_info job_run_begin(Job *job, ExecutorRef *executor) {
248+
auto invalidInfo = []{
249+
return job_run_info{ 0, OS_SIGNPOST_ID_INVALID };
250+
};
251+
248252
if (AsyncTask *task = dyn_cast<AsyncTask>(job)) {
249-
ENSURE_LOGS(0);
250-
auto id = os_signpost_id_generate(TaskLog);
253+
ENSURE_LOGS(invalidInfo());
254+
auto handle = os_signpost_id_generate(TaskLog);
255+
auto taskId = task->getTaskId();
251256
os_signpost_interval_begin(
252-
TaskLog, id, SWIFT_LOG_JOB_RUN_NAME,
257+
TaskLog, handle, SWIFT_LOG_JOB_RUN_NAME,
253258
"task=%" PRIx64
254259
" executorIdentity=%p executorImplementation=0x%" PRIxPTR,
255-
task->getTaskId(), executor->getIdentity(),
256-
executor->getRawImplementation());
257-
return id;
260+
taskId, executor->getIdentity(), executor->getRawImplementation());
261+
return { taskId, handle };
258262
}
259-
return 0;
263+
return invalidInfo();
260264
}
261265

262-
inline void job_run_end(Job *job, ExecutorRef *executor, uint64_t beginHandle) {
263-
if (AsyncTask *task = dyn_cast<AsyncTask>(job)) {
266+
inline void job_run_end(ExecutorRef *executor, job_run_info info) {
267+
if (info.handle != OS_SIGNPOST_ID_INVALID) {
264268
ENSURE_LOGS();
265269
os_signpost_interval_end(
266-
TaskLog, beginHandle, SWIFT_LOG_JOB_RUN_NAME,
270+
TaskLog, info.handle, SWIFT_LOG_JOB_RUN_NAME,
267271
"task=%" PRIx64
268272
" executorIdentity=%p executorImplementation=0x%" PRIxPTR,
269-
task->getTaskId(), executor->getIdentity(),
273+
info.taskId, executor->getIdentity(),
270274
executor->getRawImplementation());
271275
}
272276
}

stdlib/public/Concurrency/TracingStubs.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ inline void job_enqueue_global_with_delay(unsigned long long delay, Job *job) {}
6060

6161
inline void job_enqueue_main_executor(Job *job) {}
6262

63-
inline uint64_t job_run_begin(Job *job, ExecutorRef *executor) { return 0; }
63+
inline job_run_info job_run_begin(Job *job, ExecutorRef *executor) { return {}; }
6464

65-
inline void job_run_end(Job *job, ExecutorRef *executor, uint64_t beginHandle) {
65+
inline void job_run_end(ExecutorRef *executor, job_run_info info) {
6666
}
6767

6868
} // namespace trace

0 commit comments

Comments
 (0)