From 6b2138bc170828cc0fabed2f5776533c723092d1 Mon Sep 17 00:00:00 2001 From: Peter Willendrup Date: Wed, 7 Oct 2026 14:22:20 +0200 Subject: [PATCH 1/2] Fix header-key matching and other UB found in MPI review - read_table-lib: Table_ParseHeader() now matches symbols as whole words (word boundaries where the symbol starts/ends with [A-Za-z0-9_]). Before, "Vc" or "DW" could match inside the random tmpnam() file name that cif2hkl writes into its header above the real values. That gave V_0=0 (all particles absorbed) or a bogus Debye-Waller factor on whichever MPI rank drew such a name: the intermittent Test_Powders results. Neutron powder/Sqw/crystal headers of all shipped data files parse unchanged. - PowderN, Isotropic_Sqw (McStas/McXtrace), Powder_process (McStas): calloc the line list; the multiplicity check may read one entry ahead. - Pol_guide_vmirror: copy at most 6 values into r..ParToFunc[6]. - sort_absorb_last (FUNNEL): set *multiplier also on the serial path, and do not divide by zero when every particle is absorbed. Co-Authored-By: Claude Opus 5.5 --- common/lib/share/mccode-r.c | 7 +++--- common/lib/share/read_table-lib.c | 25 +++++++++++++++++++++- mcstas-comps/optics/Pol_guide_vmirror.comp | 3 +++ mcstas-comps/samples/Isotropic_Sqw.comp | 2 +- mcstas-comps/samples/PowderN.comp | 2 +- mcstas-comps/union/Powder_process.comp | 2 +- mcxtrace-comps/samples/Isotropic_Sqw.comp | 2 +- mcxtrace-comps/samples/PowderN.comp | 2 +- 8 files changed, 35 insertions(+), 10 deletions(-) diff --git a/common/lib/share/mccode-r.c b/common/lib/share/mccode-r.c index 85c317099a..7c43b772f6 100644 --- a/common/lib/share/mccode-r.c +++ b/common/lib/share/mccode-r.c @@ -4149,9 +4149,8 @@ mcstatic void norm_func(double *x, double *y, double *z) { #ifdef FUNNEL long sort_absorb_last(_class_particle* particles, _class_particle* pbuffer, long len, long buffer_len, long flag_split, long* multiplier) { #define SAL_THREADS 1024 // num parallel sections - if (len_absorbed)); - // return (no SPLIT) - if (flag_split != 1) + // return (no SPLIT, or nothing left to split) + if (flag_split != 1 || accumlen == 0) return accumlen; // SPLIT - repeat the non-absorbed block N-1 times, where len % accumlen = N + R diff --git a/common/lib/share/read_table-lib.c b/common/lib/share/read_table-lib.c index 9ddb075cc8..2e81b3b9ba 100644 --- a/common/lib/share/read_table-lib.c +++ b/common/lib/share/read_table-lib.c @@ -1323,6 +1323,29 @@ MCDETECTOR Table_Write(t_Table Table, char *file, char *xl, char *yl, #define MyNL_ARGMAX 50 #endif +static int Table_ParseHeader_wordchar(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; +} + +/* Find symbol in header as a whole word (like regex \b...\b), not case + sensitive. Word boundaries are only required where the symbol itself starts + or ends with a word character, so "sigma_a " or "e_min=" match as given. + E.g. "Vc" must not match inside a file name such as /tmp/tmp.0.aVcX9q, + which cif2hkl writes into the header before the real "Vc" line. */ +static char *Table_ParseHeader_find(char *header, char *symbol) { + size_t len = strlen(symbol); + char *pos = header; + while ((pos = (char*)strcasestr(pos, symbol))) { + int start_ok = !Table_ParseHeader_wordchar(symbol[0]) + || pos == header || !Table_ParseHeader_wordchar(pos[-1]); + int end_ok = !Table_ParseHeader_wordchar(symbol[len-1]) + || !Table_ParseHeader_wordchar(pos[len]); + if (start_ok && end_ok) return pos; + pos++; + } + return NULL; +} + char **Table_ParseHeader_backend(char *header, ...){ va_list ap; char exit_flag=0; @@ -1350,7 +1373,7 @@ char **Table_ParseHeader_backend(char *header, ...){ exit_flag = 1; break; } /* search for the symbol in the header */ - pos = (char*)strcasestr(header, arg_char); + pos = Table_ParseHeader_find(header, arg_char); if (pos) { char *eol_pos; eol_pos = strchr(pos+strlen(arg_char), '\n'); diff --git a/mcstas-comps/optics/Pol_guide_vmirror.comp b/mcstas-comps/optics/Pol_guide_vmirror.comp index 128ccf5cb5..4cab4364af 100644 --- a/mcstas-comps/optics/Pol_guide_vmirror.comp +++ b/mcstas-comps/optics/Pol_guide_vmirror.comp @@ -164,16 +164,19 @@ INITIALIZE } n_index = sizeof (rPar) / sizeof (rPar[0]); + if (n_index > 6) n_index = 6; // r..ParToFunc holds at most 6 values for (i_index = 0; i_index < n_index; i_index++) { rParToFunc[i_index] = rPar[i_index]; } n_index = sizeof (rUpPar) / sizeof (rUpPar[0]); + if (n_index > 6) n_index = 6; // r..ParToFunc holds at most 6 values for (i_index = 0; i_index < n_index; i_index++) { rUpParToFunc[i_index] = rUpPar[i_index]; } n_index = sizeof (rDownPar) / sizeof (rDownPar[0]); + if (n_index > 6) n_index = 6; // r..ParToFunc holds at most 6 values for (i_index = 0; i_index < n_index; i_index++) { rDownParToFunc[i_index] = rDownPar[i_index]; } diff --git a/mcstas-comps/samples/Isotropic_Sqw.comp b/mcstas-comps/samples/Isotropic_Sqw.comp index b49d4b1332..ac3cf93702 100644 --- a/mcstas-comps/samples/Isotropic_Sqw.comp +++ b/mcstas-comps/samples/Isotropic_Sqw.comp @@ -525,7 +525,7 @@ SHARE printf ("Isotropic_sqw: Converting %ld %s from %s into S(q,w) data\n", size, flag_qSq ? "S(q)" : "powder lines", sqwTable.filename); }); /* allocate line_data array */ - list = (struct line_data*)malloc (size * sizeof (struct line_data)); + list = (struct line_data*)calloc (size, sizeof (struct line_data)); // zeroed: the multiplicity check may look one entry ahead for (i = 0; i < size; i++) { double j = 0, d = 0, w = 0, DWfactor = 0, F2 = 0, Sq = -1, q = 0; diff --git a/mcstas-comps/samples/PowderN.comp b/mcstas-comps/samples/PowderN.comp index b1f976293d..d23fbaa97d 100644 --- a/mcstas-comps/samples/PowderN.comp +++ b/mcstas-comps/samples/PowderN.comp @@ -648,7 +648,7 @@ SHARE info->compname);); } /* allocate line_data array */ - list = (struct line_data*)malloc (size * sizeof (struct line_data)); + list = (struct line_data*)calloc (size, sizeof (struct line_data)); // zeroed: the multiplicity check may look one entry ahead if (!list) { exit (fprintf (stderr, "PowderN: %s: Error: Could not allocate line array\n", info->compname)); } diff --git a/mcstas-comps/union/Powder_process.comp b/mcstas-comps/union/Powder_process.comp index f0683e1b54..ea0fc22599 100755 --- a/mcstas-comps/union/Powder_process.comp +++ b/mcstas-comps/union/Powder_process.comp @@ -260,7 +260,7 @@ SHARE "WARNING: but F2 unit is set to barns=0 (fm^2). Intensity might be 100 times too low.\n", info->compname);); /* allocate line_data array */ - list = (struct line_data_union*)malloc (size * sizeof (struct line_data_union)); + list = (struct line_data_union*)calloc (size, sizeof (struct line_data_union)); // zeroed: the multiplicity check may look one entry ahead if (!list) exit (fprintf (stderr, "Powder_process: ERROR allocating memory (read_line_data_union)\n")); diff --git a/mcxtrace-comps/samples/Isotropic_Sqw.comp b/mcxtrace-comps/samples/Isotropic_Sqw.comp index 6be82ca25d..7012256001 100644 --- a/mcxtrace-comps/samples/Isotropic_Sqw.comp +++ b/mcxtrace-comps/samples/Isotropic_Sqw.comp @@ -475,7 +475,7 @@ SHARE printf ("Isotropic_Sqw: Converting %ld %s from %s into S(q,w) data\n", size, flag_qSq ? "S(q)" : "powder lines", sqwTable.filename); }); /* allocate line_data array */ - list = (struct line_data*)malloc (size * sizeof (struct line_data)); + list = (struct line_data*)calloc (size, sizeof (struct line_data)); // zeroed: the multiplicity check may look one entry ahead for (i = 0; i < size; i++) { double j = 0, d = 0, w = 0, DWfactor = 0, F2 = 0, Sq = -1, q = 0; diff --git a/mcxtrace-comps/samples/PowderN.comp b/mcxtrace-comps/samples/PowderN.comp index 554381372c..7558469172 100644 --- a/mcxtrace-comps/samples/PowderN.comp +++ b/mcxtrace-comps/samples/PowderN.comp @@ -469,7 +469,7 @@ SHARE info->compname);); } /* allocate line_data array */ - list = (struct line_data*)malloc (size * sizeof (struct line_data)); + list = (struct line_data*)calloc (size, sizeof (struct line_data)); // zeroed: the multiplicity check may look one entry ahead for (i = 0; i < size; i++) { /* printf("Reading in line %i\n",i);*/ From da367a6fa7dc5a3ef817df83c4f136e25635f903 Mon Sep 17 00:00:00 2001 From: Peter Willendrup Date: Wed, 7 Oct 2026 14:42:23 +0200 Subject: [PATCH 2/2] reflectivity-lib: handle optional header keys, fix KINEMATIC indices - COATING: "d" is optional (no shipped coating file has it); default to 0 instead of strtod(NULL). It used to be found by accident, as the "d" in "density", which also gave 0. With whole-word header matching this crashed all McXtrace mirror/coating instruments. - COATING type guess: do not strtol(NULL) when a file has no "Z". - KINEMATIC: four keys are parsed, so check indices 0-3 (it checked a non-existent 5th one and always failed) and read gamma/lambda/rho_ab from 1-3 instead of 2-4. Co-Authored-By: Claude Opus 5.5 --- mcxtrace-comps/share/reflectivity-lib.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mcxtrace-comps/share/reflectivity-lib.c b/mcxtrace-comps/share/reflectivity-lib.c index c6ece75c25..2d38b88fb9 100644 --- a/mcxtrace-comps/share/reflectivity-lib.c +++ b/mcxtrace-comps/share/reflectivity-lib.c @@ -182,7 +182,7 @@ int reflec_Init_File(t_Reflec *R, char *filename){ } else { stracpy(R->rc.matrl,header_parsed[0],255); R->rc.T = table; - R->rc.d = strtod(header_parsed[4], NULL); + R->rc.d = header_parsed[4] ? strtod(header_parsed[4], NULL) : 0; /* d is optional */ R->rc.rho=strtod(header_parsed[3],NULL); R->rc.Z=strtod(header_parsed[1],NULL); R->rc.At=strtod(header_parsed[2],NULL); @@ -221,16 +221,16 @@ int reflec_Init_File(t_Reflec *R, char *filename){ case KINEMATIC: { char **header_parsed = Table_ParseHeader(table->header, "#N=", "#gamma=", "#lambda=", "#rho_ab=", NULL); - if (! (header_parsed[0] && header_parsed[1] && header_parsed[2] && header_parsed[3] && header_parsed[4])){ + if (! (header_parsed[0] && header_parsed[1] && header_parsed[2] && header_parsed[3])){ fprintf(stderr,"Error: %s: Could not parse file \"%s\".\n", REFLIBNAME,filename); exit(-1); } R->rk.N = strtol(header_parsed[0], NULL, 10); - R->rk.Gamma = strtod(header_parsed[2], NULL); - R->rk.Lambda = strtod(header_parsed[3], NULL); - R->rk.rho_AB = strtod(header_parsed[4], NULL); + R->rk.Gamma = strtod(header_parsed[1], NULL); + R->rk.Lambda = strtod(header_parsed[2], NULL); + R->rk.rho_AB = strtod(header_parsed[3], NULL); break; } @@ -291,7 +291,7 @@ enum reflec_Type get_table_reflec_type(t_Table *t){ if(!type){ /*type of reflectivity file is not specified - try to guess instead*/ header_parsed = Table_ParseHeader(t->header,"Z",NULL); - long Z = strtol(header_parsed[0],NULL,0); + long Z = header_parsed[0] ? strtol(header_parsed[0],NULL,0) : 0; if(Z >0 && Z<116){ /*this appears to be a coating file similar to Pt.txt and Be.txt (in the mcxtrace data library*/ printf("INFO: %s: Datafile type not explicit in reflectivity file %s.\n"