From 753b101e541ba9349b4b1f9e7167ed0d73bf8f97 Mon Sep 17 00:00:00 2001 From: Landon Curt Noll Date: Thu, 14 Aug 2025 18:23:07 -0700 Subject: [PATCH] prep for 2.16.0.0 release with value address arithmetic restrictions --- CHANGES | 36 +++++++++++++++++-- Makefile.config | 4 +-- cal/regress.cal | 95 +++++++++++++++++++++++++++++-------------------- errtbl.c | 2 ++ help/address | 75 ++++++++++++++++++++++++++++++++++---- opcodes.c | 4 +-- value.c | 23 ++++++++++++ version.h | 13 +++++++ 8 files changed, 201 insertions(+), 51 deletions(-) diff --git a/CHANGES b/CHANGES index 622ec18..7e9db85 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,37 @@ +The following are the changes from calc version 2.15.1.2 to 2.16.0.0: + + Starting with calc version 2.16.0.0, the ability to perform arithmetic + on addresses of values in calc objects has been greatly restricted. + + Most arithmetic on of value addresses could easily cause calc to + crash. For example, prior to calc version 2.16.0.0, the following + command was likely to crash calc: + + calc '*((&.)+1e9)' + + Subtracting two value addresses is permitted, however there is NO + guarantee that the address of a value will remain consistent across + calc runs. Addresses of values depend on the circumstances of when + the calc values were formed. + + The above restrictions and caveats apply to addresses of values. + Such restrictions and caveats to NOT apply to the addresses of + octets, NOR to the addresses within strings. If isptr(x) == 2, then + x is value-pointer and the above mentioned restrictions and caveats apply. + + See "help address" for more information on value address arithmetic. + + Added E_INVALID_DEREF (10610) error code to indicate the invalid + dereferencing a non-variable. + + Added E_INVALID_ADDR_OP (10611) error code to indicate an invalid + arithmetic address operation. + + We plan to let this most recent change settle down before performing + the calc v2 to calc v3 fork. Therefore, version 2.16.1.0 will form + the basis for the calc v2 to calc v3 fork. + + The following are the changes from calc version 2.15.1.2 to 2.15.1.2: Removed use of HAVE_MEMMOVE as well have_memmv.c. Removed the @@ -17,8 +51,6 @@ The following are the changes from calc version 2.15.1.1 to 2.15.1.1: Put full date range (1989-2025) of calc source into version.h. - Version 2.16.0.0 will form the basis for the calc v2 to calc v3 fork. - The following are the changes from calc version 2.15.0.7 to 2.15.1.0: diff --git a/Makefile.config b/Makefile.config index 9c86a46..7b05eae 100644 --- a/Makefile.config +++ b/Makefile.config @@ -1245,11 +1245,11 @@ EXT= # The calc version in the form of x.y.z.w # -VERSION= 2.15.1.2 +VERSION= 2.16.0.0 # The calc major version in the form of x.y.z # -VER= 2.15.1 +VER= 2.16.0 # Names of shared libraries with versions # diff --git a/cal/regress.cal b/cal/regress.cal index 2c3b390..2696790 100644 --- a/cal/regress.cal +++ b/cal/regress.cal @@ -7916,7 +7916,7 @@ print '180: define g7500e(a,b) = *a = b' */ define test_ptr() { - local a, b, c, A, B, B1, B2, M, L, p, q, p0, q0; + local a, b, c, A, B, B1, B2, M, L, p, q, r, p0, q0; print '7500: Beginning test_ptr'; @@ -8042,22 +8042,21 @@ define test_ptr() p = &M[0], *p = 5; print '7587: p = &M[0], *p = 5;'; vrfy(M[0] == 5, '7588: M[0] == 5'); - *++p = 6; - print '7589: *++p = 6;'; - vrfy(M[1] == 6, '7590: M[1] == 6'); - q = p++; - print '7591: q = p++;'; + q = &M[1], *q = 6; + print '7589: q = &M[1], *q = 6;'; + vrfy(M[1] == 6, '7588: M[1] == 6'); + vrfy(M[0] == 5, '7588: M[0] == 5'); vrfy(q == &M[1], '7592: q == &M[1]'); - vrfy(p == &M[2], '7593: p == &M[2]'); + vrfy(p == &M[0], '7593: p == &M[0]'); quomod(17,5,*q,*p); print '7594: quomod(17,5,*p,*q);'; - vrfy(M[1] == 3 && M[2] == 2, '7595: M[1] == 3 && M[2] == 2'); + vrfy(M[1] == 3 && M[0] == 2, '7595: M[1] == 3 && M[0] == 2'); swap(*p, *q); print '7596: swap(*p, *q);'; - vrfy(M[1] == 2 && M[2] == 3, '7597: M[1] == 2 && M[2] == 3'); + vrfy(M[1] == 2 && M[0] == 3, '7597: M[1] == 2 && M[0] == 3'); A = *M = {7,8}; print '7598: A = *M = {7,8};'; - vrfy(M == (mat[4] = {5,2,3,4}), '7599: M == (mat[4] = {5,2,3,4})'); + vrfy(M == (mat[4] = {3,2,3,4}), '7599: M == (mat[4] = {3,2,3,4})'); vrfy(A == (mat[4] = {7,8,3,4}), '7600: A == (mat[4] = {7,8,3,4})'); /* Values which point to themselves */ @@ -8069,46 +8068,64 @@ define test_ptr() print '7603: A = &B, B = &A;'; vrfy(**A == A && ***A == B, '7604: **A == A && ***A == B'); - /* Testing functions that return pointers */ + /* verify arithmetic value address operation restrictions */ - M[3] = 7; - print '7605: M[3] = 7;'; - vrfy(*g7500b(&M[1], 2) == 7, '7606: *g7500b(&M[1], 2) == 7'); - - *g7500b(&M[1], 2) = 8; - print '7607: *g7500b(&M[1], 2) = 8;'; - vrfy(M[3] == 8, '7608: M[3] == 8'); - M[3] = list(9,10); - print '7609: M[3] = list(9,10);'; - vrfy((*g7500b(&M[1], 2))[[1]] == 10, - '7610: (*g7500b(&M[1], 2))[[1]] == 10'); + M = mat[10] = {0,1,2,3,4,5,6,7,8,9}; + print '7605: M = mat[10] = {0,1,2,3,4,5,6,7,8,9};'; + /* NOTE: The next set of tests will trigger 10 errors by design */ + ecnt += 10; + print '7606: ecnt += 10'; + p = &M[3]; + print '7607: p = &M[3]'; + ++p; + print '7608: ++p'; + vrfy(p == error("E_INVALID_ADDR_OP"), + '7609: p == error("E_INVALID_ADDR_OP")'); + p = &M[4]; + print '7610: p = &M[4]'; + p++; + print '7611: p++'; + vrfy(p == error("E_INVALID_ADDR_OP"), + '7612: p == error("E_INVALID_ADDR_OP")'); + p = &M[5]; + print '7613: p = &M[5]'; + q = &M[8]; + print '7614: q = &M[8]'; + vrfy(q+5 == error("E_INVALID_ADDR_OP"), + '7615: q+5 == error("E_INVALID_ADDR_OP")'); + vrfy(q-7 == error("E_INVALID_ADDR_OP"), + '7616: q-7 == error("E_INVALID_ADDR_OP")'); + vrfy(p+q == error("E_INVALID_ADDR_OP"), + '7617: p+q == error("E_INVALID_ADDR_OP")'); + r = p-q; + print '7618: r = p-q;'; /* Testing number and string pointers */ a = 24, b = 4 * 6, c = 4!; - print '7611: a = 24, b = 4 * 6, c= 4!;'; - vrfy(isptr(&*a) == 4, '7612: isptr(&*a) == 4'); - vrfy(&*a == &24, '7613: &*a == &24'); - vrfy(&*a == &*b, '7614: &*a == &*b'); - vrfy(&*a != &*c, '7615: &*a != &*c'); + print '7619: a = 24, b = 4 * 6, c= 4!;'; + vrfy(isptr(&*a) == 4, '7620: isptr(&*a) == 4'); + vrfy(&*a == &24, '7621: &*a == &24'); + vrfy(&*a == &*b, '7622: &*a == &*b'); + vrfy(&*a != &*c, '7623: &*a != &*c'); a = b = "abc", c = strcat("a", "bc"); - print '7616: a = b = "abc", c = strcat("a", "bc");'; - vrfy(isptr(&*a) == 3, '7617: isptr(&*a) == 3'); - vrfy(&*a == &"abc", '7618: &*a == &"abc"'); - vrfy(&*a == &*b, '7619: &*a == &*b'); - vrfy(&*a != &*c, '7620: &*a != &*c'); + print '7624: a = b = "abc", c = strcat("a", "bc");'; + vrfy(isptr(&*a) == 3, '7625: isptr(&*a) == 3'); + vrfy(&*a == &"abc", '7626: &*a == &"abc"'); + vrfy(&*a == &*b, '7627: &*a == &*b'); + vrfy(&*a != &*c, '7628: &*a != &*c'); a = c; - print '7621: a = c;'; - vrfy(&*a == &*c, '7622: &*a == &*c'); + print '7629: a = c;'; + vrfy(&*a == &*c, '7630: &*a == &*c'); /* Verifying null-ness of freed numbers */ c = 4!, p = &*c, free(c); - print '7623: c = 4!, p = &*c, free(c)'; - vrfy(isnull(*p), '7624: isnull(*p)'); + print '7631: c = 4!, p = &*c, free(c)'; + vrfy(isnull(*p), '7632: isnull(*p)'); - print '7625: Ending test_ptr'; + print '7633: Ending test_ptr'; } print '181: parsed test_ptr()'; @@ -9165,8 +9182,8 @@ read -once "test8900.special"; print '8902: about to run test8900(1,,8903)'; testnum = test8900(1,,8903); print "8941: ecnt ==", ecnt; -print '8942: ecnt = 214;' -ecnt = 214; +print '8942: ecnt = 224;' +ecnt = 224; /* diff --git a/errtbl.c b/errtbl.c index 4f44e36..036ed45 100644 --- a/errtbl.c +++ b/errtbl.c @@ -759,6 +759,8 @@ CONST struct errtbl error_table[] = { 10607, "E_LOG_5", "Cannot calculate log of 0" }, { 10608, "E_LOG2_4", "Cannot calculate log base 2 of 0" }, { 10609, "E_LOGN_6", "Cannot calculate log base n of 0" }, + { 10610, "E_INVALID_DEREF", "Dereferencing a non-variable" }, + { 10611, "E_INVALID_ADDR_OP", "Invalid arithmetic address operation" }, /* IMPORTANT NOTE: add new entries above here and be sure their errnum numeric value is consecutive! */ /* The next NULL entry must be last */ diff --git a/help/address b/help/address index 5a499aa..d52de5c 100644 --- a/help/address +++ b/help/address @@ -113,7 +113,7 @@ DESCRIPTION (the case of f(0) is exceptional since 27 + 0 simply copies the 27 rather than creating a new number value). Here it is clearly more - efficient to use + efficient to use: ; A = B = C = f(2); @@ -125,11 +125,74 @@ DESCRIPTION and number-pointer respectively, and 0 otherwise. The output when addresses are printed consists of a description (o_ptr, - v_ptr, s_ptr, n_ptr) followed by : and the address printed in - %p format. + v_ptr, s_ptr, n_ptr) followed by : and the address printed in %p format. - Iteration of & is not permitted; &&X causes a "non-variable operand" - scan error. + Iteration of & is not permitted; &(&X) causes a "Addressing + non-addressable type" scan error. + +RESTRICTIONS ON VALUE ADDRESS ARITHMETIC + Most arithmetic operations on addresses of values are not permitted. + For example one may not perform arithmetic operations (like +, ++, + -, --, *, /, etc.) on an address of a value with a numeric value. + + The reason why most arithmetic operations were removed from calc + because arithmetic on addresses of calc objects could easily cause + calc to crash. Consider this horrific expression that was allowed + in calc prior to version 2.16.0.0: + + ; *((&.)+1e9) /* INVALID: for calc version >= 2.16.0.0 */ + + Prior to calc version 2.16.0.0, some arithmetic operations on + addresses of values were permitted. Starting with calc version + 2.16.0.0, arithmetic operations between an addresses and an integer + will return an error code: + + ; ++p; /* INVALID: will be set to E_INVALID_ADDR_OP */ + ; r = q - 2; /* INVALID: will be set to E_INVALID_ADDR_OP */ + + Adding two value addresses is NOT permitted: + + ; mat A[3]; + ; p = &A[1]; + ; q = &A[2]; + ; s = p + q; /* INVALID: will be set to E_INVALID_ADDR_OP */ + + Subtracting two value addresses is permitted, however there is NO + guarantee that the address of a value will remain consistent across + calc runs. Addresses of values depend on the circumstances of when + the calc values were formed. For example: + + ; a = 3; pa = &a; + ; /* ... stuff happens ... */ + ; b = 5; pb = &b; + ; t = pa - pb; /* permitted: value depends on circumstances */ + + Subtracting value addresses of components with the same compound + object, such as a matrix, is permitted. For example: + + ; mat M[10] = {0,1,2,3,4,5,6,7,8,9}; + ; i = 4; j = 5; + ; p = &M[i]; q = &M[j]; + ; v = p - q; /* permitted: value depends on circumstances */ + + Comparison between two value addresses is permitted, however the + same caveats apply as to the subtracting of addresses: + + ; if (p < q) { + ;; print "permitted: p is less than q"; + ;; } else if (p == q) { + ;; print "permitted: p is equal to q"; + ;; } else { + ;; print "permitted: p is greater than q"; + ;; } + + PLEASE NOTE: + + The above restrictions and caveats apply to addresses of values. + Such restrictions and caveats to NOT apply to the addresses of + octets, NOR to the addresses within strings. If isptr(x) == 2, + then x is value-pointer and the above mentioned restrictions + and caveats apply it. EXAMPLE Addresses for particular systems may differ from those displayed here. @@ -160,7 +223,7 @@ LINK LIBRARY SEE ALSO dereference, isptr -## Copyright (C) 1999-2006,2021 Landon Curt Noll +## Copyright (C) 1999-2006,2021,2025 Landon Curt Noll ## ## Calc is open software; you can redistribute it and/or modify it under ## the terms of the version 2.1 of the GNU Lesser General Public License diff --git a/opcodes.c b/opcodes.c index eb5de71..815daf6 100644 --- a/opcodes.c +++ b/opcodes.c @@ -959,8 +959,8 @@ o_deref(FUNC *UNUSED(fp)) return; } if (stack->v_type != V_ADDR) { - math_error("Dereferencing a non-variable"); - not_reached(); + *stack = error_value(E_INVALID_DEREF); + return; } vp = vp->v_addr; switch (vp->v_type) { diff --git a/value.c b/value.c index 8d88f8a..a990ac8 100644 --- a/value.c +++ b/value.c @@ -434,6 +434,8 @@ addvalue(VALUE *v1, VALUE *v2, VALUE *vres) *vres = error_value(E_STRADD); return; case TWOVAL(V_VPTR, V_NUM): +#if defined(PERMIT_DANGEROUS_ADDRESS_ARITHMETIC) + /* NOTE: Defining PERMIT_DANGEROUS_ADDRESS_ARITHMETIC is NOT supported! */ q = v2->v_num; if (qisfrac(q)) { math_error("Adding non-integer to address"); @@ -442,6 +444,12 @@ addvalue(VALUE *v1, VALUE *v2, VALUE *vres) i = qtoi(q); vres->v_addr = v1->v_addr + i; vres->v_type = V_VPTR; +#else /* Disable arithmetic on addresses */ + *vres = error_value(E_INVALID_ADDR_OP); +#endif /* Disable arithmetic on addresses */ + return; + case TWOVAL(V_VPTR, V_VPTR): + *vres = error_value(E_INVALID_ADDR_OP); return; case TWOVAL(V_OPTR, V_NUM): q = v2->v_num; @@ -515,6 +523,8 @@ subvalue(VALUE *v1, VALUE *v2, VALUE *vres) *vres = error_value(E_STRSUB); return; case TWOVAL(V_VPTR, V_NUM): +#if defined(PERMIT_DANGEROUS_ADDRESS_ARITHMETIC) + /* NOTE: Defining PERMIT_DANGEROUS_ADDRESS_ARITHMETIC is NOT supported! */ q = v2->v_num; if (qisfrac(q)) { math_error("Subtracting non-integer from address"); @@ -523,6 +533,9 @@ subvalue(VALUE *v1, VALUE *v2, VALUE *vres) i = qtoi(q); vres->v_addr = v1->v_addr - i; vres->v_type = V_VPTR; +#else /* Disable arithmetic on addresses */ + *vres = error_value(E_INVALID_ADDR_OP); +#endif /* Disable arithmetic on addresses */ return; case TWOVAL(V_OPTR, V_NUM): q = v2->v_num; @@ -1407,7 +1420,12 @@ incvalue(VALUE *vp, VALUE *vres) vres->v_octet = vp->v_octet + 1; break; case V_VPTR: +#if defined(PERMIT_DANGEROUS_ADDRESS_ARITHMETIC) + /* NOTE: Defining PERMIT_DANGEROUS_ADDRESS_ARITHMETIC is NOT supported! */ vres->v_addr = vp->v_addr + 1; +#else /* Disable arithmetic on addresses */ + *vres = error_value(E_INVALID_ADDR_OP); +#endif /* Disable arithmetic on addresses */ break; default: if (vp->v_type > 0) @@ -1443,7 +1461,12 @@ decvalue(VALUE *vp, VALUE *vres) vres->v_octet = vp->v_octet - 1; break; case V_VPTR: +#if defined(PERMIT_DANGEROUS_ADDRESS_ARITHMETIC) + /* NOTE: Defining PERMIT_DANGEROUS_ADDRESS_ARITHMETIC is NOT supported! */ vres->v_addr = vp->v_addr - 1; +#else /* Disable arithmetic on addresses */ + *vres = error_value(E_INVALID_ADDR_OP); +#endif /* Disable arithmetic on addresses */ break; default: if (vp->v_type >= 0) diff --git a/version.h b/version.h index 4edc992..cb48788 100644 --- a/version.h +++ b/version.h @@ -66,5 +66,18 @@ #define MAJOR_PATCH 1 /* level 3: major software version level */ #define MINOR_PATCH 2 /* level 4: minor software version level */ +/* + * Defining PERMIT_DANGEROUS_ADDRESS_ARITHMETIC is NOT supported! + * + * If someone were to be a foolish as to permit dangerous address arithmetic, then we + * negate the major version to further "disavow" such a calc compile. + */ +#if defined(PERMIT_DANGEROUS_ADDRESS_ARITHMETIC) +# undef TEMP_MAJOR_VER +# define TEMP_MAJOR_VER MAJOR_VER +# undef MAJOR_VER +# define MAJOR_VER (-TEMP_MAJOR_VER) +# undef TEMP_MAJOR_VER +#endif #endif /* !INCLUDE_VERSION_H*/