Skip to content

Commit 8e77263

Browse files
authored
[llvm][mustache] Fix UB in ASTNode::render() (#142249)
The current implementation set a reference to a nullptr, leading to all kinds of problems. Instead, we can check the various uses to ensure we don't deref invalid memory, and improve the logic for how contexts are passed to children, since that was also subtly wrong in some cases.
1 parent fd452da commit 8e77263

File tree

1 file changed

+33
-24
lines changed

1 file changed

+33
-24
lines changed

llvm/lib/Support/Mustache.cpp

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
//===----------------------------------------------------------------------===//
88
#include "llvm/Support/Mustache.h"
99
#include "llvm/ADT/SmallVector.h"
10-
#include "llvm/Support/Error.h"
1110
#include "llvm/Support/raw_ostream.h"
1211
#include <sstream>
1312

@@ -23,6 +22,13 @@ static bool isFalsey(const json::Value &V) {
2322
(V.getAsArray() && V.getAsArray()->empty());
2423
}
2524

25+
static bool isContextFalsey(const json::Value *V) {
26+
// A missing context (represented by a nullptr) is defined as falsey.
27+
if (!V)
28+
return true;
29+
return isFalsey(*V);
30+
}
31+
2632
static Accessor splitMustacheString(StringRef Str) {
2733
// We split the mustache string into an accessor.
2834
// For example:
@@ -560,68 +566,71 @@ void toMustacheString(const json::Value &Data, raw_ostream &OS) {
560566
}
561567
}
562568

563-
void ASTNode::render(const json::Value &Data, raw_ostream &OS) {
564-
ParentContext = &Data;
569+
void ASTNode::render(const json::Value &CurrentCtx, raw_ostream &OS) {
570+
// Set the parent context to the incoming context so that we
571+
// can walk up the context tree correctly in findContext().
572+
ParentContext = &CurrentCtx;
565573
const json::Value *ContextPtr = Ty == Root ? ParentContext : findContext();
566-
const json::Value &Context = ContextPtr ? *ContextPtr : nullptr;
567574

568575
switch (Ty) {
569576
case Root:
570-
renderChild(Data, OS);
577+
renderChild(CurrentCtx, OS);
571578
return;
572579
case Text:
573580
OS << Body;
574581
return;
575582
case Partial: {
576583
auto Partial = Partials.find(AccessorValue[0]);
577584
if (Partial != Partials.end())
578-
renderPartial(Data, OS, Partial->getValue().get());
585+
renderPartial(CurrentCtx, OS, Partial->getValue().get());
579586
return;
580587
}
581588
case Variable: {
582589
auto Lambda = Lambdas.find(AccessorValue[0]);
583-
if (Lambda != Lambdas.end())
584-
renderLambdas(Data, OS, Lambda->getValue());
585-
else {
590+
if (Lambda != Lambdas.end()) {
591+
renderLambdas(CurrentCtx, OS, Lambda->getValue());
592+
} else if (ContextPtr) {
586593
EscapeStringStream ES(OS, Escapes);
587-
toMustacheString(Context, ES);
594+
toMustacheString(*ContextPtr, ES);
588595
}
589596
return;
590597
}
591598
case UnescapeVariable: {
592599
auto Lambda = Lambdas.find(AccessorValue[0]);
593-
if (Lambda != Lambdas.end())
594-
renderLambdas(Data, OS, Lambda->getValue());
595-
else
596-
toMustacheString(Context, OS);
600+
if (Lambda != Lambdas.end()) {
601+
renderLambdas(CurrentCtx, OS, Lambda->getValue());
602+
} else if (ContextPtr) {
603+
toMustacheString(*ContextPtr, OS);
604+
}
597605
return;
598606
}
599607
case Section: {
600-
// Sections are not rendered if the context is falsey.
601608
auto SectionLambda = SectionLambdas.find(AccessorValue[0]);
602609
bool IsLambda = SectionLambda != SectionLambdas.end();
603-
if (isFalsey(Context) && !IsLambda)
604-
return;
605610

606611
if (IsLambda) {
607-
renderSectionLambdas(Data, OS, SectionLambda->getValue());
612+
renderSectionLambdas(CurrentCtx, OS, SectionLambda->getValue());
608613
return;
609614
}
610615

611-
if (Context.getAsArray()) {
612-
const json::Array *Arr = Context.getAsArray();
616+
if (isContextFalsey(ContextPtr))
617+
return;
618+
619+
if (const json::Array *Arr = ContextPtr->getAsArray()) {
613620
for (const json::Value &V : *Arr)
614621
renderChild(V, OS);
615622
return;
616623
}
617-
renderChild(Context, OS);
624+
renderChild(*ContextPtr, OS);
618625
return;
619626
}
620627
case InvertSection: {
621628
bool IsLambda = SectionLambdas.contains(AccessorValue[0]);
622-
if (!isFalsey(Context) || IsLambda)
623-
return;
624-
renderChild(Context, OS);
629+
if (isContextFalsey(ContextPtr) && !IsLambda) {
630+
// The context for the children remains unchanged from the parent's, so
631+
// we pass this node's original incoming context.
632+
renderChild(CurrentCtx, OS);
633+
}
625634
return;
626635
}
627636
}

0 commit comments

Comments
 (0)