Included Primer3 modifications

The C sources under primer3/src/libprimer3/ are the included copy of the open-source Primer3 v2.6.1 library, modified to make them amenable to the Python C API / Cython bindings (see also Derivative work). In addition to the pervasive build-glue and API-surface changes, we occasionally patch this included C to fix bugs that are reachable through the bindings.

This page records those intentional, behavior-affecting divergences from upstream Primer3 so they can be reviewed and re-applied if the included tree is ever updated to a newer upstream release. It is a living document: add an entry here whenever you change the included C in a way that alters behavior (pure mechanical/build changes and comments do not need to be listed).

Each entry notes the file and function, the category of change, what changed and why, and whether it is reachable from the Python API.


Safety / correctness fixes

read_boulder.cparse_intron_list bounds check

  • Category: memory-safety fix (out-of-bounds write)

  • Reachable from Python: yes — run_design / bindings.design_primers via the SEQUENCE_OVERLAP_JUNCTION_LIST and SEQUENCE_INTERNAL_OVERLAP_JUNCTION_LIST tags.

parse_intron_list wrote list[*count] and incremented *count with no check against PR_MAX_INTERVAL_ARRAY (200). The destination arrays (sa->primer_overlap_junctions, sa->intl_overlap_junctions) are fixed int[PR_MAX_INTERVAL_ARRAY] fields of seq_args, so a junction list with more than 200 integers overran the array and corrupted adjacent struct fields. The sibling interval parsers already bounds-check; this one was the outlier.

The parser now resets *count and returns 0 (its existing error signal, on which the caller appends an "Error in SEQUENCE_..._JUNCTION_LIST" message) when the array is full.

oligotm.c — reject sequences shorter than 2 nt in oligotm()

  • Category: memory-safety + correctness fix (out-of-bounds read / nonsense result)

  • Reachable from Python: yes — calc_tm / bindings.calc_tm via seqtm.

oligotm() computed len = strlen(s) - 1 and then applied the nearest-neighbor terminal penalty at s[len]. For an empty string len underflowed to -1, so the code read s[-1] (out of bounds); a 1-nt sequence produced a nonsense Tm instead of the OLIGOTM_ERROR sentinel that the function’s own ERROR: label documents for “length < 2”.

oligotm() now returns OLIGOTM_ERROR up front when strlen(s) < 2. Empty input still returns the same sentinel (now without the OOB read), and 1-nt input returns the sentinel instead of a meaningless value.


Notes

  • Line numbers are intentionally omitted; search for the function name and the primer3-py: comment that marks each patch in the source.

  • When updating the included tree from a newer upstream Primer3, check each entry above against the incoming source and re-apply any patch that upstream has not itself adopted.