dawn-cmake/src/dawn_native/CompilationMessages.cpp
Brandon Jones cef2d17385 Prevent negative error message ranges
If an error message was reported from Tint that had a end line/position
that occurred before the start line/position then the range would have
a negative length, causing the unsigned length to underflow into a very
large value. Also, specifically, if the start line was non-zero but the
end line was zero (indicating no line, and the value ranges are
constructed with by default) the for loop that adds the line offsets
would underflow and cause it to read off the end of the line list.

Clamping the end of the range to always be less than or equal to the
start of the range avoids both of these problems.

Bug: dawn:1245
Change-Id: I780a1f5acc228297cbbea86f33679d00e9153b4c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/75260
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Auto-Submit: Brandon Jones <bajones@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
2022-01-05 18:27:36 +00:00

202 lines
7.6 KiB
C++

// Copyright 2021 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dawn_native/CompilationMessages.h"
#include "common/Assert.h"
#include "dawn_native/dawn_platform.h"
#include <tint/tint.h>
namespace dawn_native {
namespace {
WGPUCompilationMessageType tintSeverityToMessageType(tint::diag::Severity severity) {
switch (severity) {
case tint::diag::Severity::Note:
return WGPUCompilationMessageType_Info;
case tint::diag::Severity::Warning:
return WGPUCompilationMessageType_Warning;
default:
return WGPUCompilationMessageType_Error;
}
}
} // anonymous namespace
OwnedCompilationMessages::OwnedCompilationMessages() {
mCompilationInfo.nextInChain = 0;
mCompilationInfo.messageCount = 0;
mCompilationInfo.messages = nullptr;
}
void OwnedCompilationMessages::AddMessageForTesting(std::string message,
wgpu::CompilationMessageType type,
uint64_t lineNum,
uint64_t linePos,
uint64_t offset,
uint64_t length) {
// Cannot add messages after GetCompilationInfo has been called.
ASSERT(mCompilationInfo.messages == nullptr);
mMessageStrings.push_back(message);
mMessages.push_back({nullptr, nullptr, static_cast<WGPUCompilationMessageType>(type),
lineNum, linePos, offset, length});
}
void OwnedCompilationMessages::AddMessage(const tint::diag::Diagnostic& diagnostic) {
// Cannot add messages after GetCompilationInfo has been called.
ASSERT(mCompilationInfo.messages == nullptr);
// Tint line and column values are 1-based.
uint64_t lineNum = diagnostic.source.range.begin.line;
uint64_t linePos = diagnostic.source.range.begin.column;
// The offset is 0-based.
uint64_t offset = 0;
uint64_t length = 0;
if (lineNum && linePos && diagnostic.source.file_content) {
const std::vector<std::string>& lines = diagnostic.source.file_content->lines;
size_t i = 0;
// To find the offset of the message position, loop through each of the first lineNum-1
// lines and add it's length (+1 to account for the line break) to the offset.
for (; i < lineNum - 1; ++i) {
offset += lines[i].length() + 1;
}
// If the end line is on a different line from the beginning line, add the length of the
// lines in between to the ending offset.
uint64_t endLineNum = diagnostic.source.range.end.line;
uint64_t endLinePos = diagnostic.source.range.end.column;
// If the range has a valid start but the end it not specified, clamp it to the start.
if (endLineNum == 0 || endLinePos == 0) {
endLineNum = lineNum;
endLinePos = linePos;
}
// Negative ranges aren't allowed
ASSERT(endLineNum >= lineNum);
uint64_t endOffset = offset;
for (; i < endLineNum - 1; ++i) {
endOffset += lines[i].length() + 1;
}
// Add the line positions to the offset and endOffset to get their final positions
// within the code string.
offset += linePos - 1;
endOffset += endLinePos - 1;
// Negative ranges aren't allowed
ASSERT(endOffset >= offset);
// The length of the message is the difference between the starting offset and the
// ending offset.
length = endOffset - offset;
}
if (diagnostic.code) {
mMessageStrings.push_back(std::string(diagnostic.code) + ": " + diagnostic.message);
} else {
mMessageStrings.push_back(diagnostic.message);
}
mMessages.push_back({nullptr, nullptr, tintSeverityToMessageType(diagnostic.severity),
lineNum, linePos, offset, length});
}
void OwnedCompilationMessages::AddMessages(const tint::diag::List& diagnostics) {
// Cannot add messages after GetCompilationInfo has been called.
ASSERT(mCompilationInfo.messages == nullptr);
for (const auto& diag : diagnostics) {
AddMessage(diag);
}
AddFormattedTintMessages(diagnostics);
}
void OwnedCompilationMessages::ClearMessages() {
// Cannot clear messages after GetCompilationInfo has been called.
ASSERT(mCompilationInfo.messages == nullptr);
mMessageStrings.clear();
mMessages.clear();
}
const WGPUCompilationInfo* OwnedCompilationMessages::GetCompilationInfo() {
mCompilationInfo.messageCount = mMessages.size();
mCompilationInfo.messages = mMessages.data();
// Ensure every message points at the correct message string. Cannot do this earlier, since
// vector reallocations may move the pointers around.
for (size_t i = 0; i < mCompilationInfo.messageCount; ++i) {
WGPUCompilationMessage& message = mMessages[i];
std::string& messageString = mMessageStrings[i];
message.message = messageString.c_str();
}
return &mCompilationInfo;
}
const std::vector<std::string>& OwnedCompilationMessages::GetFormattedTintMessages() {
return mFormattedTintMessages;
}
void OwnedCompilationMessages::AddFormattedTintMessages(const tint::diag::List& diagnostics) {
tint::diag::List messageList;
size_t warningCount = 0;
size_t errorCount = 0;
for (auto& diag : diagnostics) {
switch (diag.severity) {
case (tint::diag::Severity::Fatal):
case (tint::diag::Severity::Error):
case (tint::diag::Severity::InternalCompilerError): {
errorCount++;
messageList.add(tint::diag::Diagnostic(diag));
break;
}
case (tint::diag::Severity::Warning): {
warningCount++;
messageList.add(tint::diag::Diagnostic(diag));
break;
}
default:
break;
}
}
if (errorCount == 0 && warningCount == 0) {
return;
}
tint::diag::Formatter::Style style;
style.print_newline_at_end = false;
std::ostringstream t;
if (errorCount > 0) {
t << errorCount << " error(s) ";
if (warningCount > 0) {
t << "and ";
}
}
if (warningCount > 0) {
t << warningCount << " warning(s) ";
}
t << "generated while compiling the shader:" << std::endl
<< tint::diag::Formatter{style}.format(messageList);
mFormattedTintMessages.push_back(t.str());
}
} // namespace dawn_native