Skip to content

Conversation

@slowkow
Copy link

@slowkow slowkow commented Jan 23, 2026

Summary

This PR fixes an uninitialized member variable bug in the Builder template class constructor in starttree.h.

The isRooted member was not being initialized in the constructor's initializer list, leading to undefined behavior when the variable was later used.

The Bug

// Before (bug):
Builder(const char* nameToUse, const char *descriptionToGive)
: name(nameToUse), description(descriptionToGive), silent(false)
, isOutputToBeAppended(false), isOutputToBeZipped(false)
, precision(6), subtreeOnly(false) {  // isRooted NOT initialized!
}

The Fix

// After (fix):
Builder(const char* nameToUse, const char *descriptionToGive)
: name(nameToUse), description(descriptionToGive), silent(false)
, isOutputToBeAppended(false), isOutputToBeZipped(false)
, precision(6), isRooted(false), subtreeOnly(false) {  // isRooted now initialized
}

Impact

On some platforms (particularly Linux with GCC), the uninitialized memory could contain non-zero garbage values. When isRooted was later used in tree construction calculations, this caused:

  1. Incorrect values for degree_of_root = isRooted ? 2 : 3
  2. Tree construction loops running beyond intended iterations
  3. Segmentation faults from array out-of-bounds access

The bug was platform and algorithm dependent because:

  • Different compilers/platforms have different memory initialization behaviors
  • Different template instantiations (BIONJMatrix<NJFloat> vs RapidNJ) have different memory layouts
  • NJ/RapidNJ happened to get zero values by chance, while BIONJ got garbage

How it was discovered

This bug was discovered while integrating decenttree into an R package. The BIONJ algorithm consistently crashed with segfaults on Ubuntu/Linux while working fine on macOS. NJ and RapidNJ worked correctly on both platforms.

Testing

The fix has been verified by successfully running BIONJ on both macOS and Linux after applying this change.

The Builder template class constructor was not initializing the
isRooted member variable, leading to undefined behavior.

On some platforms (particularly Linux with GCC), the uninitialized
memory could contain non-zero garbage values. When isRooted was
later used in calculations like:

    degree_of_root = isRooted ? 2 : 3

this caused incorrect tree construction behavior. For certain
template instantiations (notably BIONJMatrix), this resulted in
segmentation faults due to array bounds violations during tree
construction.

The bug was platform and algorithm dependent because:
- Different compilers/platforms have different memory initialization
- Different template instantiations have different memory layouts
- NJ/RapidNJ happened to get zero values, while BIONJ got garbage

This fix initializes isRooted to false in the constructor's
initializer list, matching the other boolean members.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant