python3.14-scipy/bundled-highs-coverity-fixes.patch
Charalampos Stratakis 35d1d1cb22 Fix issues uncovered via coverity scanning
Related: RHEL-120814
2026-02-10 14:17:01 +01:00

380 lines
13 KiB
Diff

From e9525346e36552686c6f9266c7388791bb27cb0b Mon Sep 17 00:00:00 2001
From: fwesselm <fwesselm@mathworks.com>
Date: Wed, 11 Dec 2024 15:55:52 +0100
Subject: [PATCH 1/7] Fix detection of implied integral (already
integer-constrained) variables and implicit (continuous) integer variables
---
subprojects/highs/src/presolve/HPresolve.cpp | 24 +++++++++++++-------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/subprojects/highs/src/presolve/HPresolve.cpp b/subprojects/highs/src/presolve/HPresolve.cpp
index 5360c9e..d6dd158 100644
--- a/subprojects/highs/src/presolve/HPresolve.cpp
+++ b/subprojects/highs/src/presolve/HPresolve.cpp
@@ -223,6 +223,7 @@ void HPresolve::dualImpliedFreeGetRhsAndRowType(
}
bool HPresolve::isImpliedIntegral(HighsInt col) {
+ // check if the integer constraint on a variable is implied by the model
bool runDualDetection = true;
assert(model->integrality_[col] == HighsVarType::kInteger);
@@ -263,10 +264,15 @@ bool HPresolve::isImpliedIntegral(HighsInt col) {
if (!runDualDetection) return false;
+ bool impliedIntegral = true;
for (const HighsSliceNonzero& nz : getColumnVector(col)) {
double scale = 1.0 / nz.value();
- if (!rowCoefficientsIntegral(nz.index(), scale)) return false;
+ // if row coefficients are not integral, variable is not (implied) integral
+ bool rowIntegral = rowCoefficientsIntegral(nz.index(), scale);
+ impliedIntegral = impliedIntegral && rowIntegral;
+ if (!rowIntegral) continue;
if (model->row_upper_[nz.index()] != kHighsInf) {
+ // scale, round down and unscale right-hand side again
double rUpper =
std::abs(nz.value()) *
std::floor(model->row_upper_[nz.index()] * std::abs(scale) +
@@ -276,24 +282,26 @@ bool HPresolve::isImpliedIntegral(HighsInt col) {
model->row_upper_[nz.index()] = rUpper;
markChangedRow(nz.index());
}
- } else {
- assert(model->row_lower_[nz.index()] != -kHighsInf);
+ }
+ if (model->row_lower_[nz.index()] != -kHighsInf) {
+ // scale, round up and unscale left-hand side again
double rLower =
std::abs(nz.value()) *
- std::ceil(model->row_upper_[nz.index()] * std::abs(scale) -
+ std::ceil(model->row_lower_[nz.index()] * std::abs(scale) -
primal_feastol);
if (std::abs(model->row_lower_[nz.index()] - rLower) >
options->small_matrix_value) {
- model->row_upper_[nz.index()] = rLower;
+ model->row_lower_[nz.index()] = rLower;
markChangedRow(nz.index());
}
}
}
- return true;
+ return impliedIntegral;
}
bool HPresolve::isImpliedInteger(HighsInt col) {
+ // check if a non-integer variable is implied integer
bool runDualDetection = true;
assert(model->integrality_[col] == HighsVarType::kContinuous);
@@ -345,11 +353,11 @@ bool HPresolve::isImpliedInteger(HighsInt col) {
for (const HighsSliceNonzero& nz : getColumnVector(col)) {
double scale = 1.0 / nz.value();
if (model->row_upper_[nz.index()] != kHighsInf &&
- fractionality(model->row_upper_[nz.index()]) > primal_feastol)
+ fractionality(model->row_upper_[nz.index()] * scale) > primal_feastol)
return false;
if (model->row_lower_[nz.index()] != -kHighsInf &&
- fractionality(model->row_lower_[nz.index()]) > primal_feastol)
+ fractionality(model->row_lower_[nz.index()] * scale) > primal_feastol)
return false;
if (!rowCoefficientsIntegral(nz.index(), scale)) return false;
--
2.53.0
From 7f75cc45101d393b4247e0eec00c59dc2c46f58e Mon Sep 17 00:00:00 2001
From: Ivet Galabova <galabovaa@gmail.com>
Date: Mon, 13 Jan 2025 16:57:56 +0000
Subject: [PATCH 2/7] free gpu memory, localtermination wip
---
subprojects/highs/src/pdlp/CupdlpWrapper.cpp | 55 +++++++++++++------
.../highs/src/pdlp/cupdlp/cupdlp_linalg.c | 13 +++++
2 files changed, 51 insertions(+), 17 deletions(-)
diff --git a/subprojects/highs/src/pdlp/CupdlpWrapper.cpp b/subprojects/highs/src/pdlp/CupdlpWrapper.cpp
index 31fa31e..8d905da 100644
--- a/subprojects/highs/src/pdlp/CupdlpWrapper.cpp
+++ b/subprojects/highs/src/pdlp/CupdlpWrapper.cpp
@@ -66,8 +66,6 @@ HighsStatus solveLpCupdlp(const HighsOptions& options, HighsTimer& timer,
0.0; // true objVal = sig * c'x - offset, sig = 1 (min) or -1 (max)
double sense_origin = 1; // 1 (min) or -1 (max)
int* constraint_new_idx = NULL;
- cupdlp_float* x_origin = cupdlp_NULL;
- cupdlp_float* y_origin = cupdlp_NULL;
void* model = NULL;
void* presolvedmodel = NULL;
@@ -159,8 +157,6 @@ HighsStatus solveLpCupdlp(const HighsOptions& options, HighsTimer& timer,
// CUPDLP_CALL(LP_SolvePDHG(prob, ifChangeIntParam, intParam,
// ifChangeFloatParam, floatParam, fp));
- cupdlp_init_double(x_origin, nCols_origin);
- cupdlp_init_double(y_origin, nRows);
// Resize the highs_solution so cuPDLP-c can use it internally
highs_solution.col_value.resize(lp.num_col_);
highs_solution.row_value.resize(lp.num_row_);
@@ -208,19 +204,31 @@ HighsStatus solveLpCupdlp(const HighsOptions& options, HighsTimer& timer,
analysePdlpSolution(options, lp, highs_solution);
#endif
- free(cost);
- free(lower);
- free(upper);
- free(csc_beg);
- free(csc_idx);
- free(csc_val);
- free(rhs);
+ // free(cost);
+ // free(lower);
+ // free(upper);
+ // free(csc_beg);
+ // free(csc_idx);
+ // free(csc_val);
+ // free(rhs);
- free(x_origin);
- free(y_origin);
+ // free(constraint_new_idx);
- free(constraint_new_idx);
+ // Scaling
+#ifdef CUPDLP_CPU
+ if (scaling->rowScale != nullptr) free(scaling->rowScale);
+ if (scaling->colScale != nullptr) free(scaling->colScale);
+ free(scaling);
+
+#else
+ // free problem
+ if (scaling) {
+ scaling_clear(scaling);
+ }
+#endif
+
+#ifdef CUPDLP_CPU
free(prob->cost);
free(prob->lower);
free(prob->upper);
@@ -248,10 +256,23 @@ HighsStatus solveLpCupdlp(const HighsOptions& options, HighsTimer& timer,
free(csc_cpu->colMatElem);
free(csc_cpu);
+#endif
- if (scaling->rowScale != nullptr) free(scaling->rowScale);
- if (scaling->colScale != nullptr) free(scaling->colScale);
- free(scaling);
+ if (cost != NULL) cupdlp_free(cost);
+ if (csc_beg != NULL) cupdlp_free(csc_beg);
+ if (csc_idx != NULL) cupdlp_free(csc_idx);
+ if (csc_val != NULL) cupdlp_free(csc_val);
+ if (rhs != NULL) cupdlp_free(rhs);
+ if (lower != NULL) cupdlp_free(lower);
+ if (upper != NULL) cupdlp_free(upper);
+ if (constraint_new_idx != NULL) cupdlp_free(constraint_new_idx);
+
+ // constraint type is std::vector
+ // if (constraint_type != NULL) cupdlp_free(constraint_type);
+
+ // free memory
+ csc_clear(csc_cpu);
+ problem_clear(prob);
return HighsStatus::kOk;
}
diff --git a/subprojects/highs/src/pdlp/cupdlp/cupdlp_linalg.c b/subprojects/highs/src/pdlp/cupdlp/cupdlp_linalg.c
index 8d28043..bf3e2fe 100644
--- a/subprojects/highs/src/pdlp/cupdlp/cupdlp_linalg.c
+++ b/subprojects/highs/src/pdlp/cupdlp/cupdlp_linalg.c
@@ -801,3 +801,16 @@ void cupdlp_compute_interaction_and_movement(CUPDLPwork *w,
cupdlp_sub(w->buffer3, iterates->aty->data, iterates->atyUpdate->data, nCols);
cupdlp_dot(w, nCols, w->buffer2, w->buffer3, dInteraction);
}
+
+double get_fabs_value(double* vec, int index) {
+#ifdef CUPDLP_CPU
+ return vec[index];
+#else
+ double result = 0;
+ int status = -1;
+ get_gpu_vec_element(vec, index, &result, &status);
+ if (!status)
+ return 0;
+ return result;
+#endif
+}
\ No newline at end of file
--
2.53.0
From 9efff0fc9ae0eb870ea8f6b5742ea9e17c90de52 Mon Sep 17 00:00:00 2001
From: Julian Hall <jajhall@ed.ac.uk>
Date: Thu, 11 Dec 2025 20:23:13 +0000
Subject: [PATCH 3/7] Modify version and URL for HiGHS release
Updated version and URL in CITATION.cff
---
subprojects/highs/CITATION.cff | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/subprojects/highs/CITATION.cff b/subprojects/highs/CITATION.cff
index 0987d34..da14abc 100644
--- a/subprojects/highs/CITATION.cff
+++ b/subprojects/highs/CITATION.cff
@@ -6,14 +6,10 @@ authors:
email: HighsOpt@gmail.com
- given-names: Ivet
family-names: Galabova
-- given-names: Leona
- family-names: Gottwald
-- given-names: Michael
- family-names: Feldmeier
title: "HiGHS"
-version: 1.2.2
+version: 1.12.0
date-released: 2022-04-18
-url: "https://github.com/ERGO-Code/HiGHS/releases/tag/v1.2.2"
+url: "https://github.com/ERGO-Code/HiGHS/releases/tag/v1.12.0"
preferred-citation:
type: article
authors:
--
2.53.0
From 7d971de3169912e449b2d2f8ab00f9f7be0968f0 Mon Sep 17 00:00:00 2001
From: Charalampos Stratakis <cstratak@redhat.com>
Date: Fri, 19 Dec 2025 17:42:13 +0100
Subject: [PATCH 4/7] Fix use-after-close in writeSolution when ranging fails
Add missing return statement after returnFromWriteSolution() call
when getRangingInterface() returns an error, preventing fprintf()
from writing to an already-closed file handle.
---
subprojects/highs/src/lp_data/Highs.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/subprojects/highs/src/lp_data/Highs.cpp b/subprojects/highs/src/lp_data/Highs.cpp
index c1c3254..a175f1c 100644
--- a/subprojects/highs/src/lp_data/Highs.cpp
+++ b/subprojects/highs/src/lp_data/Highs.cpp
@@ -3272,7 +3272,7 @@ HighsStatus Highs::writeSolution(const std::string& filename,
interpretCallStatus(options_.log_options, this->getRangingInterface(),
return_status, "getRangingInterface");
if (return_status == HighsStatus::kError)
- returnFromWriteSolution(file, return_status);
+ return returnFromWriteSolution(file, return_status);
fprintf(file, "\n# Ranging\n");
writeRangingFile(file, model_.lp_, info_.objective_function_value, basis_,
solution_, ranging_, style);
--
2.53.0
From d7a19c9fad468ca4b8e094da67ea2d8cc3760e7f Mon Sep 17 00:00:00 2001
From: Charalampos Stratakis <cstratak@redhat.com>
Date: Mon, 22 Dec 2025 02:02:25 +0100
Subject: [PATCH 5/7] mip: Fix use-after-move in HighsLpRelaxation::loadModel
Save lpmodel.num_col_ before passing lpmodel via std::move to
lpsolver.passModel(). After the move, lpmodel is in a valid but
unspecified state, making access to its members undefined behavior.
Found by Coverity static analysis.
---
subprojects/highs/src/mip/HighsLpRelaxation.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/subprojects/highs/src/mip/HighsLpRelaxation.cpp b/subprojects/highs/src/mip/HighsLpRelaxation.cpp
index 4288f99..58e0e4d 100644
--- a/subprojects/highs/src/mip/HighsLpRelaxation.cpp
+++ b/subprojects/highs/src/mip/HighsLpRelaxation.cpp
@@ -222,11 +222,12 @@ void HighsLpRelaxation::loadModel() {
for (HighsInt i = 0; i != lpmodel.num_row_; ++i)
lprows.push_back(LpRow::model(i));
lpmodel.integrality_.clear();
+ HighsInt num_col = lpmodel.num_col_;
lpsolver.clearSolver();
lpsolver.clearModel();
lpsolver.passModel(std::move(lpmodel));
- colLbBuffer.resize(lpmodel.num_col_);
- colUbBuffer.resize(lpmodel.num_col_);
+ colLbBuffer.resize(num_col);
+ colUbBuffer.resize(num_col);
}
void HighsLpRelaxation::resetToGlobalDomain() {
--
2.53.0
From cba9eba8aed879bc6335d3f4d43a5cd9f891e854 Mon Sep 17 00:00:00 2001
From: Charalampos Stratakis <cstratak@redhat.com>
Date: Wed, 14 Jan 2026 04:50:19 +0100
Subject: [PATCH 6/7] BUG: initialize save_value field to fix uninitialized
memory use
When a HighsSimplexBadBasisChangeRecord is created, save_value is not
set until later in applyTabooRowOut/applyTabooVariableIn. Pushing the
struct with an uninitialized field copies garbage memory.
Found via Coverity static analysis
---
subprojects/highs/src/simplex/SimplexStruct.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/subprojects/highs/src/simplex/SimplexStruct.h b/subprojects/highs/src/simplex/SimplexStruct.h
index eb49c8c..996dcc4 100644
--- a/subprojects/highs/src/simplex/SimplexStruct.h
+++ b/subprojects/highs/src/simplex/SimplexStruct.h
@@ -258,7 +258,7 @@ struct HighsSimplexBadBasisChangeRecord {
HighsInt variable_out;
HighsInt variable_in;
BadBasisChangeReason reason;
- double save_value;
+ double save_value = 0.0;
};
#endif /* SIMPLEX_SIMPLEXSTRUCT_H_ */
--
2.53.0
From ee47ee8895c789980a1e90d8e0c17f54a69b4440 Mon Sep 17 00:00:00 2001
From: Charalampos Stratakis <cstratak@redhat.com>
Date: Wed, 28 Jan 2026 01:23:57 +0100
Subject: [PATCH 7/7] Initialize nowactiveatlower in ratiotest_textbook
The nowactiveatlower field was only set inside the conditional blocks
when a limiting constraint was found (alpha_i < result.alpha). If both
loops completed without finding a limiting constraint, the field remained
uninitialized.
Uncovered by Coverity static analysis
---
subprojects/highs/src/qpsolver/ratiotest.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/subprojects/highs/src/qpsolver/ratiotest.cpp b/subprojects/highs/src/qpsolver/ratiotest.cpp
index dd313c4..424f695 100644
--- a/subprojects/highs/src/qpsolver/ratiotest.cpp
+++ b/subprojects/highs/src/qpsolver/ratiotest.cpp
@@ -17,6 +17,7 @@ static RatiotestResult ratiotest_textbook(Runtime& rt, const QpVector& p,
RatiotestResult result;
result.limitingconstraint = -1;
result.alpha = alphastart;
+ result.nowactiveatlower = false;
// check ratio towards variable bounds
for (HighsInt j = 0; j < p.num_nz; j++) {
--
2.53.0