Advisory: buggy logxor function.

 new new list compose Reply to this message Top page
Attachments:
+ (text/plain)
+ logxor.diff (text/plain)

Delete this message
Author: Kaz Kylheku
Date:  
To: TXR Users
Subject: Advisory: buggy logxor function.
This issue affects every version of TXR that has had the logxor
function: 67 through 202.

The function calculates an incorrect result if the inputs are equal
nonzero fixnums, or a character and a nonzero fixnum.

It also calculates an incorrect result if the inputs are the same
bignum.

The patch is attached.
commit 24361dae566ef759c0923ea5baa2b6f0476374e3
Author: Kaz Kylheku <kaz@???>
Date: Sun Nov 25 10:23:16 2018 -0800

    logxor: fix seriously broken function.


    Reported by Guillome LE VAILLANT.


    * arith.c (logxor): Fix broken behavior when the arguments are
    the same nonzero fixnum, or the same bignum object.
    (logxor_old): New function: verbatim copy of previous logxor.


    * eval.c (eval_init): Register logxor intrinsic to the broken
    function if compatibility is 202 or less.


    * txr.1: Compat note added.


diff --git a/arith.c b/arith.c
index 90575fc..badc5d8 100644
--- a/arith.c
+++ b/arith.c
@@ -2461,6 +2461,49 @@ val logxor(val a, val b)
{
val c;

+  switch (TYPE_PAIR(type(a), type(b))) {
+  case TYPE_PAIR(NUM, CHR):
+  case TYPE_PAIR(CHR, NUM):
+    {
+      cnum ac = c_n(a);
+      cnum bc = c_n(b);
+      return chr(ac ^ bc);
+    }
+  case TYPE_PAIR(NUM, NUM):
+    {
+      cnum ac = c_n(a);
+      cnum bc = c_n(b);
+      return num_fast(ac ^ bc);
+    }
+  case TYPE_PAIR(BGNUM, NUM):
+    {
+      val tmp = a;
+      a = b;
+      b = tmp;
+    }
+    /* fallthrough */
+  case TYPE_PAIR(NUM, BGNUM):
+    a = bignum(c_n(a));
+    /* fallthrough */
+  case TYPE_PAIR(BGNUM, BGNUM):
+    if (a == b)
+      return zero;
+    c = make_ubignum();
+    if (mp_xor(mp(a), mp(b), mp(c)) != MP_OKAY)
+      goto bad;
+    return normalize(c);
+  default:
+    uw_throwf(error_s, lit("logxor: non-integral operands ~s ~s"), a, b, nao);
+  }
+
+bad:
+  uw_throwf(error_s, lit("logxor: operation failed on ~s ~s"), a, b, nao);
+}
+
+val logxor_old(val a, val b)
+{
+  val c;
+
   if (zerop(a) && zerop(b))
     return zero;


diff --git a/eval.c b/eval.c
index 637ed8a..0dc6855 100644
--- a/eval.c
+++ b/eval.c
@@ -6476,7 +6476,8 @@ void eval_init(void)
   reg_fun(intern(lit("in"), user_package), func_n4o(in, 2));
   reg_fun(intern(lit("logand"), user_package), func_n0v(logandv));
   reg_fun(intern(lit("logior"), user_package), func_n0v(logiorv));
-  reg_fun(intern(lit("logxor"), user_package), func_n2(logxor));
+  reg_fun(intern(lit("logxor"), user_package),
+          func_n2(if3(opt_compat && opt_compat <= 202, logxor_old, logxor)));
   reg_fun(intern(lit("logtest"), user_package), func_n2(logtest));
   reg_fun(intern(lit("lognot"), user_package), func_n2o(lognot, 1));
   reg_fun(intern(lit("logtrunc"), user_package), func_n2(logtrunc));
diff --git a/lib.h b/lib.h
index be25d68..1488239 100644
--- a/lib.h
+++ b/lib.h
@@ -749,6 +749,7 @@ val logior(val, val);
 val logandv(struct args *nlist);
 val logiorv(struct args *nlist);
 val logxor(val, val);
+val logxor_old(val, val);
 val logtest(val, val);
 val lognot(val, val);
 val logtrunc(val a, val bits);
diff --git a/txr.1 b/txr.1
index ac73168..db93a6c 100644
--- a/txr.1
+++ b/txr.1
@@ -65473,6 +65473,13 @@ of these version values, the described behaviors are provided if
 is given an argument which is equal or lower. For instance
 .code "-C 103"
 selects the behaviors described below for version 105, but not those for 102.
+.IP 202
+Up to \*(TX 202, the
+.code logxor
+function was incorrectly implemented, producing wrong results when both
+arguments are the same fixnum integer, or the same bignum object.
+The incorrect behavior is restored if 202 or earlier compatibility is
+requested.
 .IP 199
 After \*(TX 199, certain global variables that had been deprecated
 for a long time, and no longer documented, were removed. Requesting 199 or