new module 'fenv'

2023-10-26 Thread Bruno Haible
When writing the lib/fpe-trapping.h file (needed to verify the quiet vs.
signalling NaNs, which in turn was needed for testing the totalorder*
functions), I was disappointed by the complexity of this file. Ideally
it would contain just two statements:
  feclearexcept (FE_INVALID);
  feenableexcept (FE_INVALID);
but then various portability problems were encountered.

Following the Gnulib philosophy, these portability fixes should be
hidden behind a GNU-like .

Here comes the module for the  header file. More modules to
come in the next days.

There is special code for each CPU, because many CPUs have their
own quirks:
  - x86_64 has two floating point units (387 unit and SSE unit).
Recall the 387 unit's "we compute everything with 80-bits
'long double's, whether you like it or not" philosophy, that
has killed the reproducibility of numerical programs' results
for decades.
  - i386 has one or two floating point units (387 always, SSE unit often).
  - aarch64 may or may not support trapping on floating-point
exceptions.
  - arm likewise. Also armv5 has only software float routines, with
no support for floating-point exceptions and only one rounding
direction.
  - riscv does not support trapping on floating-point exceptions at all.
  - alpha does not support "inexact" floating-point exceptions well.
It also requires system calls to modify the floating-point
control word.
  - m68k has 3 bits for "Invalid operation", not just one. Also it
has 'double's that deviate from IEEE 754.
  - powerpc has its control word stored in a floating-point register,
not in an integer-like register.
  - hppa likewise.
  - sh4 has only two rounding directions, not four.
  - ia64, like x86_64 and i386, has bits that are set when traps
for a certain floating-point exception are *disabled* (the
opposite of all other CPUs).
Only mips, s390, sparc, loongarch are somewhat sane.

Bruno


2023-10-26  Bruno Haible  

fenv: Add tests.
* tests/test-fenv.c: New file.
* modules/fenv-tests: New file.

fenv: New module.
* lib/fenv.in.h: New file, based on glibc.
* m4/fenv_h.m4: New file.
* modules/fenv: New file.
* doc/posix-headers/fenv.texi: Mention the new module.

From b536e9d30d31345f197ccf219378ed3142fc6beb Mon Sep 17 00:00:00 2001
From: Bruno Haible 
Date: Fri, 27 Oct 2023 03:43:15 +0200
Subject: [PATCH 1/2] fenv: New module.

* lib/fenv.in.h: New file, based on glibc.
* m4/fenv_h.m4: New file.
* modules/fenv: New file.
* doc/posix-headers/fenv.texi: Mention the new module.
---
 ChangeLog   |   8 +
 doc/posix-headers/fenv.texi |   8 +-
 lib/fenv.in.h   | 489 
 m4/fenv_h.m4|  98 
 modules/fenv|  87 +++
 5 files changed, 686 insertions(+), 4 deletions(-)
 create mode 100644 lib/fenv.in.h
 create mode 100644 m4/fenv_h.m4
 create mode 100644 modules/fenv

diff --git a/ChangeLog b/ChangeLog
index fed7803763..b89af7a969 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2023-10-26  Bruno Haible  
+
+	fenv: New module.
+	* lib/fenv.in.h: New file, based on glibc.
+	* m4/fenv_h.m4: New file.
+	* modules/fenv: New file.
+	* doc/posix-headers/fenv.texi: Mention the new module.
+
 2023-10-25  Paul Eggert  
 
 	base32: new function isubase32; also, tune.
diff --git a/doc/posix-headers/fenv.texi b/doc/posix-headers/fenv.texi
index 2e7d10d29e..36c636d7db 100644
--- a/doc/posix-headers/fenv.texi
+++ b/doc/posix-headers/fenv.texi
@@ -3,15 +3,15 @@
 
 POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/fenv.h.html}
 
-Gnulib module: ---
+Gnulib module: fenv
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This header file is missing on some platforms:
+FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, AIX 5.1, IRIX 6.5, Cygwin 1.7.7, MSVC 9.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This header file is missing on some platforms:
-FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, AIX 5.1, IRIX 6.5, Cygwin 1.7.7, MSVC 9.
 @end itemize
diff --git a/lib/fenv.in.h b/lib/fenv.in.h
new file mode 100644
index 00..c52d0e0ccb
--- /dev/null
+++ b/lib/fenv.in.h
@@ -0,0 +1,489 @@
+/* A GNU-like .
+
+   Copyright (C) 1997-2023 Free Software Foundation, Inc.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   This file is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If 

Re: [PATCH] base32, base64: disallow non-canonical encodings

2023-10-26 Thread Pádraig Brady

On 26/10/2023 16:38, Pádraig Brady wrote:

Unconditionally disallow encodings that don't have
appropriate zero bits before padding characters.
This handles one class of encoding variations discussed at:
https://eprint.iacr.org/2022/361.pdf
Note the other variations discussed there, due to optional
padding characters are already disallowed.

* lib/base32.c: Check that discarded bits in the encoding are zero.
* lib/base64.c: Likewise.
* tests/test-base32.c: Add test cases.
* tests/test-base64.c: Likewise.


To give a little more context, this will avoid
round trip issues like the following, by failing early:

  $ echo "HelloWorld==" | base64 -d | base64
  HelloWorlQ==

In general that would make the decoder a bit better
at detecting corruption in the encoded stream,
and more resilient to attacks where users tweak the
encoded stream for nefarious purposes.

I can't see any legitimate case where one would not want/have
zero bits in this case.

Note RFC4648 says:
"In some environments, the alteration is critical and therefore
decoders MAY chose to reject an encoding if the pad bits have not
been set to zero."

So it's within spec, but I don't see a reason why
you would want the behavior of allowing non zero pad bits.

cheers,
Pádraig.



[PATCH] base32, base64: disallow non-canonical encodings

2023-10-26 Thread Pádraig Brady
Unconditionally disallow encodings that don't have
appropriate zero bits before padding characters.
This handles one class of encoding variations discussed at:
https://eprint.iacr.org/2022/361.pdf
Note the other variations discussed there, due to optional
padding characters are already disallowed.

* lib/base32.c: Check that discarded bits in the encoding are zero.
* lib/base64.c: Likewise.
* tests/test-base32.c: Add test cases.
* tests/test-base64.c: Likewise.
---
 ChangeLog   |  8 
 lib/base32.c| 18 ++
 lib/base64.c|  9 +
 tests/test-base32.c | 15 +++
 tests/test-base64.c |  9 +
 5 files changed, 59 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index fed7803763..2a42cd4217 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2023-10-26  Pádraig Brady  
+
+   base32, base64: disallow non-canonical encodings
+   * lib/base32.c: Check that discarded bits in the encoding are zero.
+   * lib/base64.c: Likewise.
+   * tests/test-base32.c: Add test cases.
+   * tests/test-base64.c: Likewise.
+
 2023-10-25  Paul Eggert  
 
base32: new function isubase32; also, tune.
diff --git a/lib/base32.c b/lib/base32.c
index 2d692a2b38..132c01df64 100644
--- a/lib/base32.c
+++ b/lib/base32.c
@@ -354,6 +354,10 @@ decode_8 (char const *restrict in, idx_t inlen,
   if (in[3] != '=' || in[4] != '=' || in[5] != '='
   || in[6] != '=' || in[7] != '=')
 return_false;
+
+  /* Reject non-canonical encodings.  */
+  if (base32_to_int[to_uchar (in[1])] & 0x03)
+return_false;
 }
   else
 {
@@ -372,6 +376,10 @@ decode_8 (char const *restrict in, idx_t inlen,
 {
   if (in[5] != '=' || in[6] != '=' || in[7] != '=')
 return_false;
+
+  /* Reject non-canonical encodings.  */
+  if (base32_to_int[to_uchar (in[3])] & 0x0F)
+return_false;
 }
   else
 {
@@ -389,6 +397,10 @@ decode_8 (char const *restrict in, idx_t inlen,
 {
   if (in[6] != '=' || in[7] != '=')
 return_false;
+
+  /* Reject non-canonical encodings.  */
+  if (base32_to_int[to_uchar (in[4])] & 0x01)
+return_false;
 }
   else
 {
@@ -415,6 +427,12 @@ decode_8 (char const *restrict in, idx_t inlen,
   --*outleft;
 }
 }
+  else
+{
+  /* Reject non-canonical encodings.  */
+  if (base32_to_int[to_uchar (in[6])] & 0x07)
+return_false;
+}
 }
 }
 }
diff --git a/lib/base64.c b/lib/base64.c
index 59a2135bf1..1f53498bb3 100644
--- a/lib/base64.c
+++ b/lib/base64.c
@@ -396,6 +396,10 @@ decode_4 (char const *restrict in, idx_t inlen,
 
   if (in[3] != '=')
 return_false;
+
+  /* Reject non-canonical encodings.  */
+  if (base64_to_int[to_uchar (in[1])] & 0x0F)
+return_false;
 }
   else
 {
@@ -416,6 +420,11 @@ decode_4 (char const *restrict in, idx_t inlen,
 {
   if (inlen != 4)
 return_false;
+
+  /* Reject non-canonical encodings.  */
+  if (base64_to_int[to_uchar (in[2])] & 0x03)
+return_false;
+
 }
   else
 {
diff --git a/tests/test-base32.c b/tests/test-base32.c
index 2e7d3c53b3..16fb982edb 100644
--- a/tests/test-base32.c
+++ b/tests/test-base32.c
@@ -256,5 +256,20 @@ main (void)
   ok = base32_decode_alloc_ctx (NULL, "AABBAA=A", 8, , );
   ASSERT (!ok);
 
+  ok = base32_decode_alloc_ctx (NULL, "FZ==", 8, , );
+  ASSERT (!ok);
+
+  ok = base32_decode_alloc_ctx (NULL, "FYXB", 8, , );
+  ASSERT (!ok);
+
+  ok = base32_decode_alloc_ctx (NULL, "FYXC5===", 8, , );
+  ASSERT (!ok);
+
+  ok = base32_decode_alloc_ctx (NULL, "FYXC4LR=", 8, , );
+  ASSERT (!ok);
+
+  ok = base32_decode_alloc_ctx (NULL, "FZ==FY==", 16, , );
+  ASSERT (!ok);
+
   return 0;
 }
diff --git a/tests/test-base64.c b/tests/test-base64.c
index 5196db09f4..0da5f99054 100644
--- a/tests/test-base64.c
+++ b/tests/test-base64.c
@@ -233,5 +233,14 @@ main (void)
   ok = base64_decode_alloc_ctx (NULL, "aax=X", 5, , );
   ASSERT (!ok);
 
+  ok = base64_decode_alloc_ctx (NULL, "SGVsbG9=", 8, , );
+  ASSERT (!ok);
+
+  ok = base64_decode_alloc_ctx (NULL, "TR==", 4, , );
+  ASSERT (!ok);
+
+  ok = base64_decode_alloc_ctx (NULL, "TWF=TWE=", 8, , );
+  ASSERT (!ok);
+
   return 0;
 }
-- 
2.41.0