Bug Summary

File:librecad/src/lib/filters/lc_embeddedtextmodel.cpp
Warning:line 473, column 12
Null pointer passed to 2nd parameter expecting 'nonnull'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name lc_embeddedtextmodel.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/home/runner/work/LibreCAD/LibreCAD/librecad/src -fcoverage-compilation-dir=/home/runner/work/LibreCAD/LibreCAD/librecad/src -resource-dir /usr/lib/llvm-21/lib/clang/21 -D _REENTRANT -D DWGSUPPORT -D MUPARSER_STATIC -D LC_WITH_OPENTYPE=0 -D QC_APPDIR=librecad -D LC_VERSION=2.2.2_alpha1-853-ge2a8c2931 -D LC_PRERELEASE=true; -D QT_NO_DEBUG -D QT_SVG_LIB -D QT_PRINTSUPPORT_LIB -D QT_WIDGETS_LIB -D QT_GUI_LIB -D QT_NETWORK_LIB -D QT_CORE_LIB -I . -I /usr/include -I ../../libraries/lciconengine -I ../../libraries/libdxfrw/src -I ../../libraries/libdxfrw/src/intern -I ../../libraries/jwwlib/src -I ../../libraries/shapelib/src -I ../../libraries/qtbidi -I cmd -I lib/actions -I lib/actions/visual_snap -I lib/actions/options -I lib/creation -I lib/debug -I lib/engine -I lib/engine/document -I lib/engine/document/blocks -I lib/engine/document/container -I lib/engine/document/dimstyles -I lib/engine/document/entities -I lib/engine/document/entities/support -I lib/engine/document/fonts -I lib/engine/document/io -I lib/engine/document/layers -I lib/engine/document/linetypes -I lib/engine/document/patterns -I lib/engine/document/selection -I lib/engine/document/textstyles -I lib/engine/document/ucs -I lib/engine/document/variables -I lib/engine/document/views -I lib/engine/clipboard -I lib/engine/overlays -I lib/engine/overlays/angles_base -I lib/engine/overlays/highlight -I lib/engine/overlays/preview -I lib/engine/overlays/references -I lib/engine/overlays/crosshair -I lib/engine/overlays/info_cursor -I lib/engine/overlays/overlay_box -I lib/engine/overlays/ucs_mark -I lib/engine/undo -I lib/engine/utils -I lib/engine/settings -I lib/fileio -I lib/filters -I lib/generators -I lib/generators/makercamsvg -I lib/generators/layers -I lib/generators/image -I lib/gui -I lib/gui/grid -I lib/gui/render -I lib/gui/render/headless -I lib/gui/render/widget -I lib/information -I lib/math -I lib/modification -I lib/selection -I lib/selection/metaentity -I lib/selection/metaentity/entities -I lib/printing -I lib/properties -I actions -I actions/dock_widgets -I actions/dock_widgets/block -I actions/dock_widgets/entity_info -I actions/dock_widgets/layer -I actions/dock_widgets/library -I actions/dock_widgets/ucs_list -I actions/drawing -I actions/drawing/draw -I actions/drawing/draw/arc -I actions/drawing/draw/circle -I actions/drawing/draw/curve -I actions/drawing/draw/spline -I actions/drawing/draw/dimensions -I actions/drawing/draw/ellipse -I actions/drawing/draw/hatch -I actions/drawing/draw/image -I actions/drawing/draw/line -I actions/drawing/draw/point -I actions/drawing/draw/rect -I actions/drawing/draw/polygon -I actions/drawing/draw/misc -I actions/drawing/draw/line/shapes -I actions/drawing/draw/line/misc -I actions/drawing/draw/line/shapes/rect -I actions/drawing/draw/line/shapes/polygon -I actions/drawing/draw/polyline -I actions/drawing/draw/text -I actions/drawing/edit -I actions/drawing/info -I actions/drawing/pick -I actions/drawing/modify -I actions/drawing/pen -I actions/drawing/rel_zero -I actions/drawing/selection -I actions/drawing/snap -I actions/drawing/zoom -I actions/file -I actions/options -I actions/print_preview -I ui -I ui/action_options -I ui/action_options/circle -I ui/action_options/curve -I ui/action_options/spline -I ui/action_options/dimensions -I ui/action_options/edit -I ui/action_options/image -I ui/action_options/info -I ui/action_options/insert -I ui/action_options/line -I ui/action_options/rect -I ui/action_options/polygon -I ui/action_options/misc -I ui/action_options/modify -I ui/action_options/ellipse -I ui/action_options/other -I ui/action_options/polyline -I ui/action_options/point -I ui/action_options/print_preview -I ui/action_options/selection -I ui/action_options/snap -I ui/action_options/text -I ui/actions -I ui/components -I ui/components/relative_position_assistant -I ui/components/comboboxes -I ui/components/containers -I ui/components/creators -I ui/components/layouts -I ui/components/pen -I ui/components/status_bar -I ui/components/toolbars -I ui/components/utils -I ui/dialogs -I ui/dialogs/actions -I ui/dialogs/actions/modify -I ui/dialogs/actions/quick_selection -I ui/dialogs/modify -I ui/dialogs/entity -I ui/dialogs/creators -I ui/dialogs/file -I ui/dialogs/file/export -I ui/dialogs/file/export/layers -I ui/dialogs/file/export/image -I ui/dialogs/file/export/makercam -I ui/dialogs/main -I ui/dialogs/settings -I ui/dialogs/settings/dimstyles -I ui/dialogs/settings/dimstyles/dimstyle_manager -I ui/dialogs/settings/dimstyles/dimstyle_manager/support -I ui/dialogs/settings/options_device -I ui/dialogs/settings/options_drawing -I ui/dialogs/settings/options_general -I ui/dialogs/settings/options_widget -I ui/dialogs/settings/shortcuts -I ui/dock_widgets -I ui/dock_widgets/block_widget -I ui/dock_widgets/command_line -I ui/dock_widgets/entity_info -I ui/dock_widgets/layer_widget -I ui/dock_widgets/layers_tree -I ui/dock_widgets/library_widget -I ui/dock_widgets/pen_palette -I ui/dock_widgets/pen_wizard -I ui/dock_widgets/property_sheet -I ui/dock_widgets/property_sheet/lib -I ui/dock_widgets/property_sheet/lib/properties -I ui/dock_widgets/property_sheet/lib/view -I ui/dock_widgets/property_sheet/lib/view/edit -I ui/dock_widgets/property_sheet/lib/widgets -I ui/dock_widgets/property_sheet/lib/widgets/sheet -I ui/dock_widgets/property_sheet/metaentity -I ui/dock_widgets/property_sheet/metaentity/entities -I ui/dock_widgets/property_sheet/metaentity/entities/document -I ui/dock_widgets/property_sheet/properties -I ui/dock_widgets/property_sheet/properties/action -I ui/dock_widgets/property_sheet/properties/bool -I ui/dock_widgets/property_sheet/properties/color -I ui/dock_widgets/property_sheet/properties/double -I ui/dock_widgets/property_sheet/properties/enum -I ui/dock_widgets/property_sheet/properties/int -I ui/dock_widgets/property_sheet/properties/layer -I ui/dock_widgets/property_sheet/properties/linetype -I ui/dock_widgets/property_sheet/properties/linewidth -I ui/dock_widgets/property_sheet/properties/rect -I ui/dock_widgets/property_sheet/properties/rsvector -I ui/dock_widgets/property_sheet/properties/string -I ui/dock_widgets/views_list -I ui/dock_widgets/ucs_list -I ui/dock_widgets/workspaces -I ui/dock_widgets/cad -I ui/main -I ui/main/init -I ui/main/persistence -I ui/main/release_check -I ui/main/support -I ui/main/fontviewer -I ui/main/workspaces -I ui/view -I main -I main/console_dxf2pdf -I test -I plugins -I ../res -I ../res/arrows -I ../res/controls -I ../res/dxf -I ../res/gdt -I ../res/icons -I ../res/images -I ../../../Qt/6.9.0/gcc_64/include -I ../../../Qt/6.9.0/gcc_64/include/QtSvg -I ../../../Qt/6.9.0/gcc_64/include/QtPrintSupport -I ../../../Qt/6.9.0/gcc_64/include/QtWidgets -I ../../../Qt/6.9.0/gcc_64/include/QtGui -I ../../../Qt/6.9.0/gcc_64/include/QtNetwork -I ../../../Qt/6.9.0/gcc_64/include/QtCore -I ../../generated/librecad/moc -I ../../generated/librecad/ui -I ../../../Qt/6.9.0/gcc_64/mkspecs/linux-g++ -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/x86_64-linux-gnu/c++/15 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15/backward -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=gnu++1z -fdeprecated-macro -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/runner/work/LibreCAD/LibreCAD/out/2026-11-06-031751-4837-1 -x c++ lib/filters/lc_embeddedtextmodel.cpp
1/****************************************************************************
2** This file is part of LibreCAD, a 2D CAD program.
3** Copyright (C) 2026 LibreCAD.org
4** Copyright (C) 2026 Dongxu Li
5** SPDX-License-Identifier: GPL-2.0-or-later
6****************************************************************************/
7#include "lc_embeddedtextmodel.h"
8#include "lc_embeddedtextxdata.h"
9#include "lc_textprofile.h"
10#include <QCryptographicHash>
11#include <algorithm>
12#include <cstring>
13#include <limits>
14#include <new>
15#include <stdexcept>
16
17namespace {
18using C = LC_EmbeddedTextModel;
19using K = C::Kind;
20using E = C::ErrorCode;
21using Bytes = C::Bytes;
22constexpr std::size_t headerSize = 64, fieldHeaderSize = 12;
23static_assert(sizeof(double) == 8 && std::numeric_limits<double>::is_iec559,
24 "Embedded models require IEEE754 binary64");
25enum class Schema {
26 Top, Source, Imported, EntityProfile, StyleProfile, HistoricalProfile,
27 EntityValue, StyleValue, Selection, Identity, CurrentStyle,
28 Outer, Embedded, Legacy, Baseline, Pending, OpaqueExtra, Deformation
29};
30struct Spec { std::uint16_t id; K kind; bool required = true; Schema child = Schema::Top; };
31struct Slice { const std::uint8_t *data = nullptr; std::size_t size = 0; };
5
Null pointer value stored to 'digest.data'
32struct View { std::uint16_t id; K kind; Slice value; };
33using Views = std::vector<View>;
34C::Error failure(E code, const char *message) { return {code, QString::fromLatin1(message), {}}; }
35bool valid(const C::Limits &l) {
36 return l.maximumBytes >= headerSize && l.maximumBytes <= 64u * 1024u * 1024u &&
37 l.maximumItems <= 1000000u && l.maximumDepth >= 1 && l.maximumDepth <= 16;
38}
39bool add(std::size_t &total, std::size_t amount, std::size_t maximum) {
40 if (total > maximum || amount > maximum - total) return false;
41 total += amount; return true;
42}
43void put(Bytes &out, std::uint64_t value, unsigned width) {
44 for (unsigned i = 0; i < width; ++i) out.push_back(std::uint8_t(value >> (8u * i)));
45}
46struct Reader {
47 Slice input;
48 std::size_t position = 0;
49 bool integer(std::uint64_t &value, unsigned width) {
50 if (position > input.size || width > input.size - position) return false;
51 value = 0;
52 for (unsigned i = 0; i < width; ++i) value |= std::uint64_t(input.data[position++]) << (8u * i);
53 return true;
54 }
55 bool slice(Slice &out, std::uint64_t size) {
56 if (position > input.size || size > input.size - position) return false;
12
Assuming field 'position' is <= field 'size'
13
Assuming the condition is false
14
Taking false branch
57 out = {input.data ? input.data + position : nullptr, std::size_t(size)};
15
Assuming field 'data' is null
16
'?' condition is false
58 position += std::size_t(size); return true;
17
Returning without writing to 'out.data'
59 }
60 bool finished() const { return position == input.size; }
61};
62std::uint64_t number(Slice s) {
63 Reader reader{s}; std::uint64_t result = 0;
64 reader.integer(result, unsigned(s.size)); return result;
65}
66const View *get(const Views &views, std::uint16_t id) {
67 const auto it = std::lower_bound(views.begin(), views.end(), id,
68 [](const View &v, std::uint16_t key) { return v.id < key; });
69 return it != views.end() && it->id == id ? &*it : nullptr;
70}
71std::uint64_t number(const Views &v, std::uint16_t id) { return number(get(v, id)->value); }
72bool present(const Views &v, std::uint16_t id) { return get(v, id) != nullptr; }
73bool emptyText(Slice s) { return s.size == 9; }
74QByteArray nullableBytes(Slice s) {
75 if (!s.data[0]) return {};
76 return QByteArray(reinterpret_cast<const char *>(s.data + 9), qsizetype(s.size - 9));
77}
78QString text(Slice s) {
79 if (!s.data[0]) return {};
80 const qsizetype count = qsizetype((s.size - 9) / 2);
81 QString result(count, Qt::Uninitialized);
82 for (qsizetype i = 0; i < count; ++i)
83 result[i] = QChar(std::uint16_t(s.data[9 + 2 * std::size_t(i)]) |
84 (std::uint16_t(s.data[10 + 2 * std::size_t(i)]) << 8));
85 return result;
86}
87
88std::vector<Spec> native(bool embedded) {
89 std::vector<Spec> s = {
90 {1,K::I32},{2,K::Bytes},{3,K::Bytes},{4,K::U32},{5,K::I32},{6,K::I32},
91 {7,K::Real},{8,K::Bool},{9,K::I32},{10,K::Bytes},{11,K::I32},{12,K::Bytes},
92 {13,K::I32},{14,K::I32},{15,K::I32},{16,K::U32},{17,K::U32},{18,K::U32},
93 {19,K::U32},{20,K::U32Array},{21,K::U32},{22,K::Bool},{23,K::RawTags},
94 {24,K::RawTagArrays},{25,K::Bool},{26,K::U64},{27,K::Bytes},{28,K::Bytes},
95 {29,K::U32},{30,K::U32},{31,K::Bool},{32,K::U32},{33,K::U32},{34,K::Bytes},
96 {35,K::Bool},{36,K::Bool},{37,K::U32},{38,K::U32},{39,K::U32},
97 {40,K::RecordArray,true,Schema::Pending},{41,K::RecordArray,true,Schema::Pending},
98 {42,K::U8},{43,K::U8},{44,K::U64},{45,K::U8},{46,K::U8},{47,K::U64},
99 {48,K::U32},{49,K::U32},{50,K::U8},{51,K::U32},{52,K::U32},{53,K::Bool},
100 {54,K::U8},{55,K::I32},{56,K::U8},{57,K::U8},{58,K::U8},{59,K::U8},
101 {60,K::U8},{61,K::U8},{62,K::U8},{63,K::U8},{64,K::I32},
102 {100,K::Coord},{101,K::Coord},{102,K::Coord},{103,K::Real},{104,K::Real},
103 {105,K::Real},{106,K::Bytes},{107,K::Real},{108,K::Real},{109,K::Real},
104 {110,K::Bytes},{111,K::I32},{112,K::I32},{113,K::I32},{114,K::U8},
105 {115,K::U8},{116,K::U32},{117,K::U64}
106 };
107 if (embedded) {
108 const std::vector<Spec> extra = {
109 {200,K::Real},{201,K::U16},{202,K::I32},{203,K::Real},{204,K::I32},
110 {205,K::I32},{206,K::Bool},{207,K::Bool},{208,K::U16},{209,K::Bool},
111 {210,K::U32},{211,K::I32},{212,K::Coord},{213,K::Coord},{214,K::Real},
112 {215,K::Real},{216,K::Real},{217,K::Real},{218,K::U16},{219,K::I32},
113 {220,K::Real},{221,K::Real},{222,K::Bool},{223,K::Bool},
114 {224,K::RealArray},{225,K::Bytes},{226,K::U16},{227,K::U32},{228,K::Bool}
115 };
116 s.insert(s.end(), extra.begin(), extra.end());
117 } else {
118 const std::vector<Spec> extra = {
119 {200,K::Bytes},{201,K::U8},{202,K::U8},{203,K::U8},{204,K::U8},
120 {205,K::I32},{206,K::Bool},{207,K::Bytes,false}
121 };
122 s.insert(s.end(), extra.begin(), extra.end());
123 }
124 return s;
125}
126const std::vector<Spec> &specs(Schema schema) {
127 static const std::vector<Spec> top = {
128 {1,K::Record,true,Schema::Source},{2,K::Record,false,Schema::Imported},
129 {3,K::Record,true,Schema::Outer},{4,K::Record,true,Schema::Embedded},
130 {5,K::Record,true,Schema::Legacy},{6,K::Record,true,Schema::Baseline},
131 {7,K::RecordArray,false,Schema::OpaqueExtra}
132 };
133 static const std::vector<Spec> source = {
134 {1,K::Utf16},{2,K::U8},{3,K::Bool},{4,K::Bool},{5,K::NullableBytes,false},
135 {6,K::U8},{7,K::U8},{8,K::Utf16},{9,K::Utf16},{10,K::U8},{11,K::U8},
136 {12,K::Utf16},{13,K::Utf16},{14,K::Utf16},{15,K::Bool},
137 {16,K::Record,true,Schema::EntityProfile},{17,K::Record,true,Schema::CurrentStyle},
138 {18,K::Record,true,Schema::Deformation}
139 };
140 static const std::vector<Spec> imported = {
141 {1,K::Utf16},{2,K::U8},{3,K::Bool},{4,K::Bool},{5,K::NullableBytes,false},
142 {6,K::U8},{7,K::U8},{8,K::Utf16},{9,K::Utf16},{10,K::U8},{11,K::U8},
143 {12,K::Utf16},{13,K::Utf16},{14,K::Utf16},
144 {16,K::Record,true,Schema::HistoricalProfile},{18,K::Record,false,Schema::Deformation}
145 };
146 static const std::vector<Spec> entityProfile = {
147 {1,K::U8},{2,K::U8},{3,K::Record,false,Schema::EntityValue},
148 {4,K::RawTags,false},{5,K::U8},{6,K::Utf16}
149 };
150 static const std::vector<Spec> styleProfile = {
151 {1,K::U8},{2,K::U8},{3,K::Record,false,Schema::StyleValue},
152 {4,K::RawTags,false},{5,K::U8},{6,K::Utf16}
153 };
154 static const std::vector<Spec> historical = {
155 {1,K::U8},{2,K::U8},{3,K::Utf16},{4,K::RawTags,false}
156 };
157 static const std::vector<Spec> value = {
158 {1,K::Record,false,Schema::Selection},{2,K::NullableBytes,false},{3,K::U8,false}
159 };
160 static const std::vector<Spec> selection = {{1,K::U8},{2,K::Record,false,Schema::Identity}};
161 static const std::vector<Spec> identity = {
162 {1,K::Utf16},{2,K::NullableBytes},{3,K::U32},{4,K::Utf16},{5,K::Utf16},{6,K::Utf16}
163 };
164 static const std::vector<Spec> currentStyle = {
165 {1,K::Utf16},{2,K::Record,true,Schema::StyleProfile},{3,K::RawTags,false}
166 };
167 static const auto outer = native(false), embedded = native(true);
168 static const std::vector<Spec> legacy = {{1,K::U8},{2,K::Bool},{3,K::Utf16}};
169 static const std::vector<Spec> baseline = {
170 {1,K::U8},{2,K::U32},{3,K::U8},{4,K::Bool},{5,K::Bool},{6,K::Bool},
171 {7,K::Record,true,Schema::Outer},{8,K::Record,false,Schema::Embedded}
172 };
173 static const std::vector<Spec> pending = {{1,K::U64},{2,K::U32},{3,K::U64},{4,K::Bool}};
174 static const std::vector<Spec> opaque = {{1,K::Utf16},{2,K::U32},{3,K::Bytes}};
175 static const std::vector<Spec> deformation = {{1,K::Real},{2,K::Real},{3,K::Real},{4,K::Real}};
176 switch (schema) {
177 case Schema::Top:return top; case Schema::Source:return source;
178 case Schema::Imported:return imported; case Schema::EntityProfile:return entityProfile;
179 case Schema::StyleProfile:return styleProfile; case Schema::HistoricalProfile:return historical;
180 case Schema::EntityValue:case Schema::StyleValue:return value;
181 case Schema::Selection:return selection; case Schema::Identity:return identity;
182 case Schema::CurrentStyle:return currentStyle; case Schema::Outer:return outer;
183 case Schema::Embedded:return embedded; case Schema::Legacy:return legacy;
184 case Schema::Baseline:return baseline; case Schema::Pending:return pending;
185 case Schema::OpaqueExtra:return opaque;
186 case Schema::Deformation:return deformation;
187 }
188 return top;
189}
190
191struct Validator {
192 const C::Limits &limits;
193 std::size_t items = 0;
194 C::Error error;
195 bool fail(E code, const char *message) { error = failure(code,message); return false; }
196 bool consume(std::uint64_t count) {
197 if (count > limits.maximumItems || !add(items, std::size_t(count), limits.maximumItems))
198 return fail(E::ResourceLimit,"Aggregate embedded model item limit exceeded");
199 return true;
200 }
201 bool fields(Slice input, std::uint64_t count, Views &out) {
202 if (count > input.size / fieldHeaderSize)
203 return fail(E::Malformed,"Embedded model field count exceeds remaining bytes");
204 if (!consume(count)) return false;
205 out.reserve(std::size_t(count)); Reader reader{input}; std::uint16_t previous = 0;
206 for (std::uint64_t index = 0; index < count; ++index) {
207 std::uint64_t id, kind, flags, size; Slice payload;
208 if (!reader.integer(id,2) || !reader.integer(kind,1) || !reader.integer(flags,1) ||
209 !reader.integer(size,8) || !reader.slice(payload,size))
210 return fail(E::Malformed,"Truncated embedded model field");
211 if (!id || id <= previous || flags)
212 return fail(E::InvalidField,"Embedded model IDs must be ascending unique and flags zero");
213 previous = std::uint16_t(id);
214 out.push_back({std::uint16_t(id),static_cast<K>(kind),payload});
215 }
216 if (!reader.finished()) return fail(E::Malformed,"Trailing embedded model record bytes");
217 return true;
218 }
219 bool record(Slice s, Schema schema, unsigned depth, Views *views = nullptr) {
220 if (depth > limits.maximumDepth) return fail(E::ResourceLimit,"Embedded model nesting limit exceeded");
221 Reader r{s}; std::uint64_t count; Slice rest;
222 if (!r.integer(count,8) || !r.slice(rest,s.size-r.position))
223 return fail(E::Malformed,"Truncated embedded model record count");
224 Views local;
225 if (!fields(rest,count,local) || !validate(local,schema,depth)) return false;
226 if (views) *views = std::move(local);
227 return true;
228 }
229 bool raw(Slice s) {
230 Reader r{s}; std::uint64_t magic,version,count;
231 if (!r.integer(magic,4) || magic != 0x5843544c || !r.integer(version,1))
232 return fail(E::Malformed,"Invalid retained raw-tag capsule");
233 if (version != 2) return fail(E::UnknownVersion,"Unknown retained raw-tag capsule version");
234 if (!r.integer(count,4)) return fail(E::Malformed,"Truncated retained raw-tag capsule count");
235 if (!consume(count)) return false;
236 const Bytes input(s.data,s.data+s.size);
237 const auto result = LC_EmbeddedTextXData::decode(input,{std::size_t(count),limits.maximumBytes});
238 if (!result) return fail(result.error.code == LC_EmbeddedTextXData::ErrorCode::ResourceLimit ?
239 E::ResourceLimit:E::Malformed,"Malformed retained raw-tag capsule");
240 return true;
241 }
242 bool field(const View &v, const Spec &spec, unsigned depth) {
243 const Slice s = v.value;
244 switch (v.kind) {
245 case K::Bool:return s.size == 1 && s.data[0] <= 1 ? true : fail(E::InvalidField,"Invalid Boolean storage");
246 case K::U8:return s.size == 1 ? true : fail(E::InvalidField,"Invalid U8 storage");
247 case K::U16:return s.size == 2 ? true : fail(E::InvalidField,"Invalid U16 storage");
248 case K::U32:case K::I32:return s.size == 4 ? true : fail(E::InvalidField,"Invalid 32-bit storage");
249 case K::U64:case K::I64:case K::Real:return s.size == 8 ? true : fail(E::InvalidField,"Invalid 64-bit storage");
250 case K::Coord:return s.size == 24 ? true : fail(E::InvalidField,"Invalid coordinate storage");
251 case K::Bytes:return true;
252 case K::Utf16:case K::NullableBytes: {
253 Reader r{s}; std::uint64_t nullness,count;
254 if (!r.integer(nullness,1) || nullness > 1 || !r.integer(count,8) ||
255 (!nullness && count) || count > (s.size-r.position)/(v.kind==K::Utf16?2u:1u) ||
256 count*(v.kind==K::Utf16?2u:1u) != s.size-r.position)
257 return fail(E::InvalidField,"Invalid null-aware string storage");
258 return true;
259 }
260 case K::Record:return record(s,spec.child,depth+1);
261 case K::U32Array:case K::RealArray: {
262 Reader r{s}; std::uint64_t count; const unsigned width = v.kind==K::U32Array?4:8;
263 if (!r.integer(count,8) || count > (s.size-r.position)/width ||
264 count*width != s.size-r.position)
265 return fail(E::Malformed,"Invalid embedded model scalar array");
266 return consume(count);
267 }
268 case K::RawTags:return raw(s);
269 case K::RecordArray:case K::RawTagArrays: {
270 Reader r{s}; std::uint64_t count;
271 if (!r.integer(count,8) || count > (s.size-r.position)/8)
272 return fail(E::Malformed,"Invalid embedded model record array count");
273 if (!consume(count)) return false;
274 if (depth+1 > limits.maximumDepth) return fail(E::ResourceLimit,"Embedded model nesting limit exceeded");
275 for (std::uint64_t i=0;i<count;++i) {
276 std::uint64_t size; Slice element;
277 if (!r.integer(size,8) || !r.slice(element,size))
278 return fail(E::Malformed,"Truncated embedded model array element");
279 if (!(v.kind==K::RecordArray ? record(element,spec.child,depth+1) : raw(element))) return false;
280 }
281 return r.finished() ? true : fail(E::Malformed,"Trailing embedded model array bytes");
282 }
283 }
284 return fail(E::UnknownKind,"Unknown embedded model field kind");
285 }
286 bool parseChild(const Views &v, std::uint16_t id, Views &out) {
287 // Previously validated; do not count fields twice or revalidate recursively.
288 Reader r{get(v,id)->value}; std::uint64_t count; r.integer(count,8);
289 out.reserve(std::size_t(count));
290 for (std::uint64_t i=0;i<count;++i) {
291 std::uint64_t fieldId,kind,flags,size; Slice payload;
292 r.integer(fieldId,2);r.integer(kind,1);r.integer(flags,1);r.integer(size,8);r.slice(payload,size);
293 out.push_back({std::uint16_t(fieldId),static_cast<K>(kind),payload});
294 }
295 return true;
296 }
297 bool value(const Views &v, bool style) {
298 LC_TextProfile::Value result;
299 result.scope = style ? LC_TextProfile::Scope::StyleDefaults:LC_TextProfile::Scope::EntityOverride;
300 if (present(v,1)) {
301 Views selection; parseChild(v,1,selection);
302 LC_TextProfile::Selection selected;
303 selected.backend = number(selection,1) ? LC_TextProfile::Backend::HarfBuzzOpenType:
304 LC_TextProfile::Backend::LegacyLff;
305 if (present(selection,2)) {
306 Views identity; parseChild(selection,2,identity);
307 selected.identity = {text(get(identity,1)->value),nullableBytes(get(identity,2)->value),
308 unsigned(number(identity,3)),text(get(identity,4)->value),
309 text(get(identity,5)->value),text(get(identity,6)->value)};
310 }
311 result.selection = std::move(selected);
312 }
313 if (present(v,2)) result.language = nullableBytes(get(v,2)->value);
314 if (present(v,3)) result.direction = static_cast<LC_TextProfile::Direction>(number(v,3));
315 if (LC_TextProfile::validate(result)) return fail(E::InvalidField,"Invalid materialized current profile value");
316 return true;
317 }
318 bool semantics(const Views &v, Schema schema) {
319 switch (schema) {
320 case Schema::Source:case Schema::Imported: {
321 if (number(v,2)>1 || number(v,6)>2 || number(v,7)>2 || number(v,10)>2 || number(v,11)>2 ||
322 (!number(v,10) && number(v,11)) || (number(v,10) && !number(v,11)) ||
323 (number(v,11)==2 && number(v,7)!=2))
324 return fail(E::InvalidField,"Invalid source convention or marker provenance");
325 if (present(v,5)) {
326 const auto bytes = nullableBytes(get(v,5)->value);
327 if (QString::fromUtf8(bytes.constData(),bytes.size()) != text(get(v,1)->value))
328 return fail(E::InvalidField,"Current/source UTF8 bytes do not decode to source");
329 }
330 return true;
331 }
332 case Schema::EntityProfile:case Schema::StyleProfile: {
333 const auto status = number(v,1), transport = number(v,5);
334 if (status>4 || number(v,2)!=(schema==Schema::StyleProfile?0u:1u) || transport>2 ||
335 present(v,3)!=(status==1) || present(v,4)!=(status>=2) ||
336 (status==4 ? (!transport || emptyText(get(v,6)->value)) : transport!=0))
337 return fail(E::InvalidField,"Inconsistent materialized profile presence/scope/transport");
338 return true;
339 }
340 case Schema::HistoricalProfile: {
341 const auto status=number(v,1), transport=number(v,2);
342 if (status>4 || transport>2 || present(v,4)!=(status!=0) ||
343 (status==4 ? (!transport || emptyText(get(v,3)->value)) : transport!=0))
344 return fail(E::InvalidField,"Inconsistent historical profile provenance");
345 return true;
346 }
347 case Schema::EntityValue:return value(v,false);
348 case Schema::StyleValue:return value(v,true);
349 case Schema::Selection:
350 return number(v,1)<=1 && present(v,2)==(number(v,1)==1) ? true :
351 fail(E::InvalidField,"Backend selection and identity must be atomic");
352 case Schema::Legacy:
353 return number(v,1)<=2 && (!number(v,2) || (number(v,1)==2 && !emptyText(get(v,3)->value))) ? true :
354 fail(E::InvalidField,"Invalid legacy embedded marker provenance");
355 case Schema::Baseline:
356 return number(v,1)<=1 && number(v,3)<=1 && present(v,8)==bool(number(v,6)) ? true :
357 fail(E::InvalidField,"Inconsistent target baseline native-body presence");
358 default:return true;
359 }
360 }
361 bool validate(const Views &v, Schema schema, unsigned depth) {
362 const auto &registry = specs(schema);
363 for (const auto &entry : v) {
364 const auto rawKind = static_cast<unsigned>(entry.kind);
365 if (rawKind<1 || rawKind>18) return fail(E::UnknownKind,"Unknown embedded model field kind");
366 const auto it=std::lower_bound(registry.begin(),registry.end(),entry.id,
367 [](const Spec &s,std::uint16_t id){return s.id<id;});
368 if (it==registry.end() || it->id!=entry.id) {
369 error = failure(E::UnknownField,"Unknown embedded model field");error.path.push_back(entry.id);return false;
370 }
371 if (it->kind != entry.kind) {
372 error=failure(E::InvalidField,"Embedded model field has the wrong registered kind");
373 error.path.push_back(entry.id);return false;
374 }
375 if (!field(entry,*it,depth)) {error.path.insert(error.path.begin(),entry.id);return false;}
376 }
377 for (const auto &spec:registry) if (spec.required && !present(v,spec.id)) {
378 error=failure(E::MissingField,"Missing required embedded model field");
379 error.path.push_back(spec.id);return false;
380 }
381 return semantics(v,schema);
382 }
383};
384
385C::Encoded pack(const C::Record &record,const C::Limits &limits,bool counted) {
386 if (!valid(limits)) return {std::nullopt,failure(E::InvalidLimits,"Invalid embedded model limits")};
387 if (record.size()>limits.maximumItems)
388 return {std::nullopt,failure(E::ResourceLimit,"Embedded model field count exceeds limit")};
389 std::size_t size=counted?8:0;
390 std::uint16_t previous=0;
391 for (const auto &f:record) {
392 if (!f.id || f.id<=previous) return {std::nullopt,failure(E::InvalidField,"Field IDs must be ascending unique")};
393 previous=f.id;
394 if (!add(size,fieldHeaderSize,limits.maximumBytes) || !add(size,f.payload.size(),limits.maximumBytes))
395 return {std::nullopt,failure(E::ResourceLimit,"Embedded model byte limit exceeded")};
396 }
397 Bytes result;result.reserve(size);if(counted)put(result,record.size(),8);
398 for (const auto &f:record) {
399 put(result,f.id,2);put(result,static_cast<std::uint8_t>(f.kind),1);put(result,0,1);
400 put(result,f.payload.size(),8);result.insert(result.end(),f.payload.begin(),f.payload.end());
401 }
402 return {std::move(result),{}};
403}
404bool opaque(E code) { return code==E::UnknownVersion || code==E::UnknownField || code==E::UnknownKind; }
405C::Decoded retained(const Bytes &input,C::Error error) {
406 return {opaque(error.code)?C::Status::Opaque:C::Status::Invalid,std::nullopt,input,std::move(error)};
407}
408C::Error allocationFailure() { return failure(E::ResourceLimit,"Embedded model allocation unavailable; input not accepted"); }
409}
410
411bool LC_EmbeddedTextModel::Field::operator==(const Field &o) const {
412 return id==o.id && kind==o.kind && payload==o.payload;
413}
414LC_EmbeddedTextModel::Encoded LC_EmbeddedTextModel::packRecord(const Record &input,const Limits &limits) {
415 try {return pack(input,limits,true);} catch(const std::bad_alloc &) {return {std::nullopt,allocationFailure()};}
416 catch(const std::length_error &) {return {std::nullopt,allocationFailure()};}
417}
418LC_EmbeddedTextModel::Encoded LC_EmbeddedTextModel::packRecordArray(const std::vector<Record> &input,const Limits &limits) {
419 try {
420 if (!valid(limits)) return {std::nullopt,failure(E::InvalidLimits,"Invalid embedded model limits")};
421 if(input.size()>limits.maximumItems) return {std::nullopt,failure(E::ResourceLimit,"Record array item limit exceeded")};
422 std::size_t size=8,items=input.size();std::vector<Bytes> payloads;payloads.reserve(input.size());
423 for(const auto &record:input) {
424 if(!add(items,record.size(),limits.maximumItems))
425 return {std::nullopt,failure(E::ResourceLimit,"Record array field limit exceeded")};
426 auto payload=pack(record,limits,true);if(!payload)return payload;
427 if(!add(size,8,limits.maximumBytes)||!add(size,payload.bytes->size(),limits.maximumBytes))
428 return {std::nullopt,failure(E::ResourceLimit,"Record array byte limit exceeded")};
429 payloads.push_back(std::move(*payload.bytes));
430 }
431 Bytes result;result.reserve(size);put(result,input.size(),8);
432 for(const auto &payload:payloads){put(result,payload.size(),8);result.insert(result.end(),payload.begin(),payload.end());}
433 return {std::move(result),{}};
434 } catch(const std::bad_alloc &) {return {std::nullopt,allocationFailure()};}
435 catch(const std::length_error &) {return {std::nullopt,allocationFailure()};}
436}
437LC_EmbeddedTextModel::Encoded LC_EmbeddedTextModel::encode(const Record &input,const Limits &limits) {
438 try {
439 auto body=pack(input,limits,false);if(!body)return body;
440 std::size_t size=headerSize;
441 if(!add(size,body.bytes->size(),limits.maximumBytes))
442 return {std::nullopt,failure(E::ResourceLimit,"Complete embedded model byte limit exceeded")};
443 Validator validator{limits,0,{}};Views views;
444 if(!validator.fields({body.bytes->data(),body.bytes->size()},input.size(),views) ||
445 !validator.validate(views,Schema::Top,1)) return {std::nullopt,std::move(validator.error)};
446 const auto digest=QCryptographicHash::hash(QByteArrayView(reinterpret_cast<const char *>(body.bytes->data()),
447 qsizetype(body.bytes->size())),QCryptographicHash::Sha256);
448 Bytes output;output.reserve(size);output.insert(output.end(),{'L','C','E','M'});
449 put(output,EnvelopeVersion,2);put(output,ModelSchemaVersion,2);
450 put(output,headerSize,4);put(output,body.bytes->size(),8);
451 put(output,input.size(),4);output.insert(output.end(),digest.begin(),digest.end());put(output,0,8);
452 output.insert(output.end(),body.bytes->begin(),body.bytes->end());return {std::move(output),{}};
453 } catch(const std::bad_alloc &) {return {std::nullopt,allocationFailure()};}
454 catch(const std::length_error &) {return {std::nullopt,allocationFailure()};}
455}
456LC_EmbeddedTextModel::Decoded LC_EmbeddedTextModel::decode(const Bytes &input,const Limits &limits) {
457 try {
458 if(!valid(limits)) return {Status::Refused,std::nullopt,std::nullopt,failure(E::InvalidLimits,"Invalid embedded model limits")};
1
Taking false branch
459 if(input.size()>limits.maximumBytes)
2
Assuming the condition is false
3
Taking false branch
460 return {Status::Refused,std::nullopt,std::nullopt,failure(E::ResourceLimit,"Complete embedded model byte limit exceeded")};
461 Reader r{{input.data(),input.size()}};Slice magic,digest,body;
4
Calling implicit default constructor for 'Slice'
6
Returning from default constructor for 'Slice'
462 std::uint64_t envelope,schema,header,bodyBytes,count,reserved;
463 if(!r.slice(magic,4) || std::memcmp(magic.data,"LCEM",4) ||
7
Assuming the condition is false
464 !r.integer(envelope,2)||!r.integer(schema,2)||!r.integer(header,4)||
8
Assuming the condition is false
465 !r.integer(bodyBytes,8)||!r.integer(count,4)||!r.slice(digest,32)||!r.integer(reserved,8))
9
Assuming the condition is false
10
Assuming the condition is false
11
Calling 'Reader::slice'
18
Returning from 'Reader::slice'
19
Assuming the condition is false
466 return retained(input,failure(E::Malformed,"Truncated or invalid embedded model header"));
467 if(envelope!=EnvelopeVersion || schema!=ModelSchemaVersion)
20
Assuming 'envelope' is equal to 'EnvelopeVersion'
21
Assuming 'schema' is equal to 'ModelSchemaVersion'
468 return retained(input,failure(E::UnknownVersion,"Unsupported embedded model version; retained without migration"));
469 if(header!=headerSize || reserved || !r.slice(body,bodyBytes) || !r.finished())
22
Assuming 'header' is equal to 'headerSize'
23
Assuming 'reserved' is 0
24
Taking false branch
470 return retained(input,failure(E::Malformed,"Invalid embedded model length/reserved/trailing storage"));
471 const auto actual=QCryptographicHash::hash(QByteArrayView(reinterpret_cast<const char *>(body.data),qsizetype(body.size)),
472 QCryptographicHash::Sha256);
473 if(std::memcmp(actual.constData(),digest.data,32))
25
Null pointer passed to 2nd parameter expecting 'nonnull'
474 return retained(input,failure(E::DigestMismatch,"Embedded model body digest mismatch"));
475 Validator validator{limits,0,{}};Views views;
476 if(!validator.fields(body,count,views)||!validator.validate(views,Schema::Top,1))
477 return retained(input,std::move(validator.error));
478 Record result;result.reserve(views.size());
479 for(const auto &view:views) result.push_back({view.id,view.kind,Bytes(view.value.data,view.value.data+view.value.size)});
480 return {Status::Known,std::move(result),std::nullopt,{}};
481 } catch(const std::bad_alloc &) {return {Status::Refused,std::nullopt,std::nullopt,allocationFailure()};}
482 catch(const std::length_error &) {return {Status::Refused,std::nullopt,std::nullopt,allocationFailure()};}
483}