Skip to content

Commit e06299f

Browse files
Fix a possible use-after-free with platform cert verification (#2692)
Fix a possible use-after-free with platform cert verification by using a unique_ptr in the flat_hash_set of pending validations. The flat_hash_set does not ensure pointer stability, but the validation thread holds a pointer to the PendingVerification, which is problematic. This PR makes PendingVerification non-moveable and non-copyable which avoids this problem. There is also another potential use-after free in that the task posted to the dispatcher deletes the PendingValidation, but the PendingValidation touches member variables after the call to post. Reordered the call to post to avoid this. Fixes #2691 Signed-off-by: Ryan Hamilton rch@google.com
1 parent 15388a2 commit e06299f

2 files changed

Lines changed: 20 additions & 27 deletions

File tree

library/common/extensions/cert_validator/platform_bridge/platform_bridge_cert_validator.cc

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,11 @@ ValidationResults PlatformBridgeCertValidator::doVerifyCertChain(
8080
} else {
8181
host = host_name;
8282
}
83-
PendingValidation validation(*this, std::move(certs), host, std::move(transport_socket_options),
84-
std::move(callback));
85-
auto insert_result = validations_.insert(std::move(validation));
86-
ASSERT(insert_result.second);
87-
PendingValidation& ref = const_cast<PendingValidation&>(*insert_result.first);
88-
std::thread verification_thread(&PendingValidation::verifyCertsByPlatform, &ref);
83+
auto validation = std::make_unique<PendingValidation>(
84+
*this, std::move(certs), host, std::move(transport_socket_options), std::move(callback));
85+
PendingValidation* validation_ptr = validation.get();
86+
validations_.insert(std::move(validation));
87+
std::thread verification_thread(&PendingValidation::verifyCertsByPlatform, validation_ptr);
8988
std::thread::id thread_id = verification_thread.get_id();
9089
validation_threads_[thread_id] = std::move(verification_thread);
9190
return {ValidationResults::ValidationStatus::Pending, absl::nullopt, absl::nullopt};
@@ -138,7 +137,16 @@ void PlatformBridgeCertValidator::PendingValidation::verifyCertsByPlatform() {
138137
void PlatformBridgeCertValidator::PendingValidation::postVerifyResultAndCleanUp(
139138
bool success, absl::string_view error_details, uint8_t tls_alert,
140139
OptRef<Stats::Counter> error_counter) {
140+
ENVOY_LOG(trace,
141+
"Finished platform cert validation for {}, post result callback to network thread",
142+
host_name_);
143+
144+
if (parent_.platform_validator_->release_validator) {
145+
parent_.platform_validator_->release_validator();
146+
}
141147
std::weak_ptr<size_t> weak_alive_indicator(parent_.alive_indicator_);
148+
149+
// Once this task runs, `this` will be deleted so this must be the last statement in the file.
142150
result_callback_->dispatcher().post([this, weak_alive_indicator, success,
143151
error = std::string(error_details), tls_alert, error_counter,
144152
thread_id = std::this_thread::get_id()]() {
@@ -152,15 +160,8 @@ void PlatformBridgeCertValidator::PendingValidation::postVerifyResultAndCleanUp(
152160
const_cast<Stats::Counter&>(error_counter.ref()).inc();
153161
}
154162
result_callback_->onCertValidationResult(success, error, tls_alert);
155-
parent_.validations_.erase(*this);
163+
parent_.validations_.erase(this);
156164
});
157-
ENVOY_LOG(trace,
158-
"Finished platform cert validation for {}, post result callback to network thread",
159-
host_name_);
160-
161-
if (parent_.platform_validator_->release_validator) {
162-
parent_.platform_validator_->release_validator();
163-
}
164165
}
165166

166167
} // namespace Tls

library/common/extensions/cert_validator/platform_bridge/platform_bridge_cert_validator.h

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,22 +73,15 @@ class PlatformBridgeCertValidator : public CertValidator, Logger::Loggable<Logge
7373
result_callback_(std::move(result_callback)),
7474
transport_socket_options_(std::move(transport_socket_options)) {}
7575

76+
// Ensure that this class is never moved or copied to guarantee pointer stability.
77+
PendingValidation(const PendingValidation&) = delete;
78+
PendingValidation(PendingValidation&&) = delete;
79+
7680
void verifyCertsByPlatform();
7781

7882
void postVerifyResultAndCleanUp(bool success, absl::string_view error_details,
7983
uint8_t tls_alert, OptRef<Stats::Counter> error_counter);
8084

81-
struct Hash {
82-
size_t operator()(const PendingValidation& p) const {
83-
return reinterpret_cast<size_t>(p.result_callback_.get());
84-
}
85-
};
86-
struct Eq {
87-
bool operator()(const PendingValidation& a, const PendingValidation& b) const {
88-
return a.result_callback_.get() == b.result_callback_.get();
89-
}
90-
};
91-
9285
private:
9386
Event::SchedulableCallbackPtr next_iteration_callback_;
9487
PlatformBridgeCertValidator& parent_;
@@ -111,8 +104,7 @@ class PlatformBridgeCertValidator : public CertValidator, Logger::Loggable<Logge
111104
// latches the platform extension API.
112105
const envoy_cert_validator* platform_validator_;
113106
absl::flat_hash_map<std::thread::id, std::thread> validation_threads_;
114-
absl::flat_hash_set<PendingValidation, PendingValidation::Hash, PendingValidation::Eq>
115-
validations_;
107+
absl::flat_hash_set<std::unique_ptr<PendingValidation>> validations_;
116108
std::shared_ptr<size_t> alive_indicator_{new size_t(1)};
117109
};
118110

0 commit comments

Comments
 (0)