Fix unsafe vec.data() usage where nullptr was assumed for empty vectors#203
Open
Copilot wants to merge 4 commits into
Open
Fix unsafe vec.data() usage where nullptr was assumed for empty vectors#203Copilot wants to merge 4 commits into
vec.data() usage where nullptr was assumed for empty vectors#203Copilot wants to merge 4 commits into
Conversation
…e checks and guard [0] accesses
Audit and fix all occurrences of std::vector<T>::data() being used in ways
that incorrectly assume the returned pointer is nullptr for empty vectors.
Changes by file:
- include/glk/type_conversion.hpp: Change both convert_to_vector overloads
from if (points == nullptr) to if (num_points <= 0) so empty-vector callers
are handled correctly regardless of what data() returns.
- include/glk/pointcloud_buffer.hpp: Guard [0].data() and ->data() accesses
in add_normals (float and double), add_normals (raw pointer), and add_buffer
template methods with empty() or num_points > 0 checks.
- include/glk/pointcloud_buffer_pcl.hpp: Guard [0].data() UB in
create_point_cloud_buffer and create_colored_point_cloud_buffer for empty
PCL clouds.
- include/glk/lines.hpp: Pass nullptr instead of colors.data() / infos.data()
when those vectors are empty in the vector-taking template constructors.
- include/glk/thin_lines.hpp: Pass nullptr instead of colors.data() /
cmap.data() when empty in vector-taking template constructors.
- include/guik/viewer/light_viewer_context.hpp: Guard colors.data() in
vector-taking update_thin_lines overloads; fix convert_to_vector color
path to respect nullptr input and use size-based empty check.
- include/guik/viewer/async_light_viewer_context.hpp: Same fixes as
light_viewer_context.hpp for async variants of update_points and
update_thin_lines.
- src/glk/pointcloud_buffer.cpp: Guard ->data() and [0].data() UB in
Eigen::Vector{3,4}f constructors, add_color, add_intensity, and
update_{points,color}_with_indices.
- src/glk/thin_lines.cpp: Guard vertices->data() and colors->data() with
num_vertices > 0 checks in all Eigen::Vector3f* constructors.
- src/glk/normal_distributions.cpp: Guard means[0].data()/covs[0].data()
with num_points > 0 in the float,4 template specialization.
- src/glk/glsl_shader.cpp: Replace vectors[0].data() with
reinterpret_cast<const float*>(vectors.data()) in set_uniform overloads
for Vector2/3/4f to avoid UB on empty vectors.
- src/glk/pointnormals_buffer.cpp: Guard vertices[0].data()/normals[0].data()
with empty() checks in the vector-taking template constructor.
Agent-Logs-Url: https://github.com/koide3/iridescence/sessions/2970d6ba-4151-4f2b-8d86-67e1548a9c3a
Co-authored-by: koide3 <31344317+koide3@users.noreply.github.com>
…ify num_points check in add_intensity Agent-Logs-Url: https://github.com/koide3/iridescence/sessions/2970d6ba-4151-4f2b-8d86-67e1548a9c3a Co-authored-by: koide3 <31344317+koide3@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Audit and fix misuse of std::vector<T>::data() in repository
Fix unsafe May 1, 2026
vec.data() usage where nullptr was assumed for empty vectors
koide3
requested changes
May 1, 2026
koide3
left a comment
Owner
There was a problem hiding this comment.
Please always use brackets for if clauses even if it's one line. Please also apply clang-format.
Agent-Logs-Url: https://github.com/koide3/iridescence/sessions/7ad7339d-704c-43f9-80ad-26dbc5a3e87b Co-authored-by: koide3 <31344317+koide3@users.noreply.github.com>
Contributor
Author
Done in df4d4b6. Added braces to all single-line |
koide3
approved these changes
May 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
std::vector::data()antipatternsinclude/glk/type_conversion.hpp: Change bothconvert_to_vectoroverloads fromif (points == nullptr)toif (num_points <= 0)include/glk/pointcloud_buffer.hpp: Guard[0].data()UB inadd_normals,add_buffertemplate methodsinclude/glk/pointcloud_buffer_pcl.hpp: Guard[0].data()UB in PCL cloud helpersinclude/glk/lines.hpp: Passnullptrinstead ofcolors.data()/infos.data()when those vectors are emptyinclude/glk/thin_lines.hpp: Passnullptrinstead ofcolors.data()/cmap.data()when emptyinclude/guik/viewer/light_viewer_context.hpp: Guardcolors.data()in vector-taking overloads; fixconvert_to_vectorcolor pathinclude/guik/viewer/async_light_viewer_context.hpp: Same fixes for async variantssrc/glk/pointcloud_buffer.cpp: Guard->data()and[0].data()UB in constructors and methodssrc/glk/thin_lines.cpp: Guardvertices->data()withnum_vertices > 0checks; also guardconvert_scalarstemporary.data()on lines 91/94src/glk/normal_distributions.cpp: Guardmeans[0].data()/covs[0].data()withnum_points > 0src/glk/glsl_shader.cpp: Replacevectors[0].data()withreinterpret_cast<const float*>(vectors.data())src/glk/pointnormals_buffer.cpp: Guardvertices[0].data()/normals[0].data()withempty()checksconvert_scalarstemporary.data()and clarifiednum_points <= 0check inadd_intensityOriginal prompt
Problem
std::vector<T>::data()is not guaranteed to returnnullptrwhen the vector is empty. The C++ standard only guarantees that the returned pointer points to the first element ifsize() > 0; otherwise the return value may be any value (including a non-null sentinel pointer used by the allocator). Any code that callsvec.data()and then checks the result againstnullptr(or passes it to a function that interpretsnullptras "no data") is therefore buggy and the bug only manifests with certain standard library implementations or after the vector has been resized down.Please audit the entire
koide3/iridescencerepository for this antipattern and fix every occurrence.What to look for
Search both
include/andsrc/(and any examples) for patterns such as:something_vec.data()being passed to an API where the receiving code checks the pointer fornullptrto decide whether to read it.fooitself doesif (data == nullptr) { ... handle empty ... }instead of relying onn == 0.vec.data()and assigns the result, then later checks the pointer to decide whether the vector had elements.(const T* data, int num)and treatdata == nullptras a special "empty / allocate uninitialized" case (e.g. ininclude/glk/type_conversion.hpp, theconvert_to_vector(const Eigen::Matrix<...>* points, int num_points)overloads have an early-return branchif (points == nullptr) { return std::vector<...>(num_points); }). When such a function is called asconvert_to_vector(some_vec.data(), some_vec.size())with an emptysome_vec, the early-return branch may or may not trigger depending on the implementation, leading to non-deterministic behavior. Any caller passingvec.data()to such APIs is at risk.vec.data()is used and the buffer code interpretsnullptrspecifically (e.g.glBufferData, customset_data(size_t, const void*),ShaderStorageBuffer, vertex/index buffers, etc.). Calling these withvec.data()of an empty vector may upload from a garbage non-null pointer.Eigen::Map<...>(vec[0].data(), ...)orvec.data()->data()- these are undefined behavior when the vector is empty, regardless of the nullptr issue. While auditing, also flag and guard these with anempty()check.How to fix
For each occurrence, choose the most appropriate fix:
if (vec.empty()) { return ...; }guard at the call site before dereferencing or passingvec.data().(ptr, size): make the implementation rely onsize == 0rather thanptr == nullptrto detect "no data". Update the API's internalif (ptr == nullptr)checks toif (num <= 0)(or||-combine them defensively).vec.empty() ? nullptr : vec.data()only when the receiving API contractually acceptsnullptras "no data" AND that contract is documented. Otherwise, simply guard the entire call withif (!vec.empty()).Eigen::Mapconstructions overvec[0].data(), wrap inif (!vec.empty())so the map is only built when there is at least one element.Concrete starting points (non-exhaustive — please search the whole repo)
include/glk/type_conversion.hpp— the twoconvert_to_vector(const Eigen::Matrix<...>* points, int num_points)overloads (around lines 50–93) checkif (points == nullptr)to decide whether to return a default-constructed vector. Any caller doingconvert_to_vector(vec.data(), vec.size())on a possibly-emptyvecis unsafe. Fix the function to usenum_points <= 0as the empty condition, and additionally guard theEigen::Map<...>(converted[0].data(), ...)/points[0].data()accesses with anum_points <= 0early-return so we never indexpoints[0]orconverted[0]when the size is zero.include/glk/type_conversion.hpp—convert_scalars(const Src* data, int num_data)doesstd::copy(data, data + num_data, ...). If a caller passesvec.data()for an emptyvec,datacould be a non-null garbage pointer;std::copywithnum_data == 0is fine, but if any caller separately checks fornullptr, it is not. Audit callers.*.cpp/*.hppundersrc/andinclude/for the regex\.data\(\)and review each call. Particularly look in:src/glk/*.cpp(vertex buffers, index buffers,shader_storage_buffer.cpp,texture.cpp,mesh*.cpp,pointcloud_buffer.cpp,thin_lines.cpp,lines.cpp,normal_distributions.cpp, etc.)src/guik/**/*.cpp(e.g.plot_data.cppplottingxs.data(),ys.data())make_*/ loader helpers that forwardvec.data()to a(ptr, size)API.points[0].data(),vec[0].data(), and&vec[0]patterns — these are UB on empty vectors and should be guard...This pull request was created from Copilot chat.