Fix Undefined Behaviour

All caused by calling Castable::As<> on nullptr objects.

Bug: tint:760
Change-Id: I0a408b3cd58086cfeab5a1af34d643f50f304948
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/49523
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
Ben Clayton 2021-04-30 10:16:45 +00:00 committed by Commit Bot service account
parent 30c03a5d50
commit 9481156eb9
6 changed files with 42 additions and 25 deletions

View File

@ -173,13 +173,20 @@ inline bool IsAnyOf(FROM* obj) {
/// @see CastFlags /// @see CastFlags
template <typename TO, int FLAGS = 0, typename FROM = detail::Infer> template <typename TO, int FLAGS = 0, typename FROM = detail::Infer>
inline TO* As(FROM* obj) { inline TO* As(FROM* obj) {
using castable = auto* as_castable = static_cast<CastableBase*>(obj);
typename std::conditional<std::is_const<FROM>::value, const CastableBase,
CastableBase>::type;
auto* as_castable = static_cast<castable*>(obj);
return Is<TO, FLAGS>(obj) ? static_cast<TO*>(as_castable) : nullptr; return Is<TO, FLAGS>(obj) ? static_cast<TO*>(as_castable) : nullptr;
} }
/// @returns obj dynamically cast to the type `TO` or `nullptr` if
/// this object does not derive from `TO`.
/// @param obj the object to cast from
/// @see CastFlags
template <typename TO, int FLAGS = 0, typename FROM = detail::Infer>
inline const TO* As(const FROM* obj) {
auto* as_castable = static_cast<const CastableBase*>(obj);
return Is<TO, FLAGS>(obj) ? static_cast<const TO*>(as_castable) : nullptr;
}
/// CastableBase is the base class for all Castable objects. /// CastableBase is the base class for all Castable objects.
/// It is not encouraged to directly derive from CastableBase without using the /// It is not encouraged to directly derive from CastableBase without using the
/// Castable helper template. /// Castable helper template.

View File

@ -132,10 +132,10 @@ ast::ConstructorExpression* ProgramBuilder::ConstructValueFilledWith(
typ::Type ProgramBuilder::TypesBuilder::MaybeCreateTypename( typ::Type ProgramBuilder::TypesBuilder::MaybeCreateTypename(
typ::Type type) const { typ::Type type) const {
if (auto* alias = type.ast->As<ast::Alias>()) { if (auto* alias = As<ast::Alias>(type.ast)) {
return {builder->create<ast::TypeName>(alias->symbol()), type.sem}; return {builder->create<ast::TypeName>(alias->symbol()), type.sem};
} }
if (auto* str = type.ast->As<ast::Struct>()) { if (auto* str = As<ast::Struct>(type.ast)) {
return {builder->create<ast::TypeName>(str->name()), type.sem}; return {builder->create<ast::TypeName>(str->name()), type.sem};
} }
return type; return type;

View File

@ -1396,7 +1396,7 @@ Maybe<ParserImpl::FunctionHeader> ParserImpl::function_header() {
return_type = type.value; return_type = type.value;
} }
if (return_type.ast->Is<ast::Void>()) { if (Is<ast::Void>(return_type.ast)) {
// crbug.com/tint/677: void has been removed from the language // crbug.com/tint/677: void has been removed from the language
deprecated(tok.source(), deprecated(tok.source(),
"omit '-> void' for functions that do not return a value"); "omit '-> void' for functions that do not return a value");

View File

@ -15,6 +15,7 @@
#ifndef SRC_SEM_INFO_H_ #ifndef SRC_SEM_INFO_H_
#define SRC_SEM_INFO_H_ #define SRC_SEM_INFO_H_
#include <type_traits>
#include <unordered_map> #include <unordered_map>
#include "src/debug.h" #include "src/debug.h"
@ -26,6 +27,9 @@ namespace sem {
/// Info holds all the resolved semantic information for a Program. /// Info holds all the resolved semantic information for a Program.
class Info { class Info {
/// Placeholder type used by Get() to provide a default value for EXPLICIT_SEM
using InferFromAST = std::nullptr_t;
public: public:
/// Constructor /// Constructor
Info(); Info();
@ -44,14 +48,18 @@ class Info {
/// Get looks up the semantic information for the AST or type node `node`. /// Get looks up the semantic information for the AST or type node `node`.
/// @param node the AST or type node /// @param node the AST or type node
/// @returns a pointer to the semantic node if found, otherwise nullptr /// @returns a pointer to the semantic node if found, otherwise nullptr
template <typename AST_OR_TYPE, template <typename SEM = InferFromAST,
typename SEM = SemanticNodeTypeFor<AST_OR_TYPE>> typename AST_OR_TYPE = CastableBase,
const SEM* Get(const AST_OR_TYPE* node) const { typename RESULT =
std::conditional_t<std::is_same<SEM, InferFromAST>::value,
SemanticNodeTypeFor<AST_OR_TYPE>,
SEM>>
const RESULT* Get(const AST_OR_TYPE* node) const {
auto it = map.find(node); auto it = map.find(node);
if (it == map.end()) { if (it == map.end()) {
return nullptr; return nullptr;
} }
return it->second->template As<SEM>(); return As<RESULT>(it->second);
} }
/// Add registers the semantic node `sem_node` for the AST or type node /// Add registers the semantic node `sem_node` for the AST or type node

View File

@ -627,8 +627,7 @@ Output DecomposeStorageAccess::Run(const Program* in, const DataMap&) {
for (auto* node : ctx.src->ASTNodes().Objects()) { for (auto* node : ctx.src->ASTNodes().Objects()) {
if (auto* ident = node->As<ast::IdentifierExpression>()) { if (auto* ident = node->As<ast::IdentifierExpression>()) {
// X // X
auto* expr = sem.Get(ident); if (auto* var = sem.Get<sem::VariableUser>(ident)) {
if (auto* var = expr->As<sem::VariableUser>()) {
if (var->Variable()->StorageClass() == ast::StorageClass::kStorage) { if (var->Variable()->StorageClass() == ast::StorageClass::kStorage) {
// Variable to a storage buffer // Variable to a storage buffer
state.AddAccesss(ident, { state.AddAccesss(ident, {

View File

@ -148,19 +148,22 @@ Output FirstIndexOffset::Run(const Program* in, const DataMap& data) {
// Fix up all references to the builtins with the offsets // Fix up all references to the builtins with the offsets
ctx.ReplaceAll([=, &ctx](ast::Expression* expr) -> ast::Expression* { ctx.ReplaceAll([=, &ctx](ast::Expression* expr) -> ast::Expression* {
auto* sem = ctx.src->Sem().Get(expr); if (auto* sem = ctx.src->Sem().Get(expr)) {
if (auto* user = sem->As<sem::VariableUser>()) { if (auto* user = sem->As<sem::VariableUser>()) {
auto it = builtin_vars.find(user->Variable()); auto it = builtin_vars.find(user->Variable());
if (it != builtin_vars.end()) { if (it != builtin_vars.end()) {
return ctx.dst->Add(ctx.CloneWithoutTransform(expr), return ctx.dst->Add(
ctx.dst->MemberAccessor(buffer_name, it->second)); ctx.CloneWithoutTransform(expr),
ctx.dst->MemberAccessor(buffer_name, it->second));
}
} }
} if (auto* access = sem->As<sem::StructMemberAccess>()) {
if (auto* access = sem->As<sem::StructMemberAccess>()) { auto it = builtin_members.find(access->Member());
auto it = builtin_members.find(access->Member()); if (it != builtin_members.end()) {
if (it != builtin_members.end()) { return ctx.dst->Add(
return ctx.dst->Add(ctx.CloneWithoutTransform(expr), ctx.CloneWithoutTransform(expr),
ctx.dst->MemberAccessor(buffer_name, it->second)); ctx.dst->MemberAccessor(buffer_name, it->second));
}
} }
} }
// Not interested in this experssion. Just clone. // Not interested in this experssion. Just clone.