linux: add support for uname26 personality

Message ID 1449098562-9513-1-git-send-email-aurelien@aurel32.net
State New, archived
Headers

Commit Message

Aurelien Jarno Dec. 2, 2015, 11:22 p.m. UTC
  When the kernel is running under the uname26 personality (for programs
that cannot handle "Linux 3.0"), it maps version 3.x to 2.6.40+x and
4.x to 2.6.60+x. When the GNU libc is configured with --enable-kernel=3.x
or 4.x and uname26 personality is enabled, binaries get abort with a
"FATAL: kernel too old message", even if the kernel actually supports
such a binary.

This patch fixes the issue by converting the value in 2.6.x format back
to the 3.x or 4.X format.
---
 ChangeLog                           |  6 ++++++
 sysdeps/unix/sysv/linux/dl-sysdep.c | 26 +++++++++++++++++++++++---
 2 files changed, 29 insertions(+), 3 deletions(-)
  

Comments

Andreas Schwab Dec. 3, 2015, 8:59 a.m. UTC | #1
Aurelien Jarno <aurelien@aurel32.net> writes:

> +static int
> +convert_from_uname26 (int version)
> +{
> +  if ((version & 0xffff00) == 0x020600)
> +    {
> +      /* 2.6.40+x to 3.x */
> +      if ((version & 0xff) >= 60)
> +	version += 0x020000 - 60;
> +      /* 2.6.60+x to 4.x */
> +      else if ((version & 0xff) >= 40)
> +	version += 0x010000 - 40;

The comments are backwards.

Andreas.
  
Florian Weimer Dec. 3, 2015, 9:12 a.m. UTC | #2
On 12/03/2015 12:22 AM, Aurelien Jarno wrote:
> When the kernel is running under the uname26 personality (for programs
> that cannot handle "Linux 3.0"), it maps version 3.x to 2.6.40+x and
> 4.x to 2.6.60+x. When the GNU libc is configured with --enable-kernel=3.x
> or 4.x and uname26 personality is enabled, binaries get abort with a
> "FATAL: kernel too old message", even if the kernel actually supports
> such a binary.

I'm sorry, I don't think this is the correct way to handle this.  The
reason is that

> +/* When the kernel is running under the uname26 personality (for programs
> +   that cannot handle "Linux 3.0"), it maps version 3.x to 2.6.40+x and
> +   4.x to 2.6.60+x.  We need to convert that back to the standard version
> +   numbering to be able to compare versions.  */
> +static int
> +convert_from_uname26 (int version)
> +{
> +  if ((version & 0xffff00) == 0x020600)
> +    {
> +      /* 2.6.40+x to 3.x */
> +      if ((version & 0xff) >= 60)
> +	version += 0x020000 - 60;
> +      /* 2.6.60+x to 4.x */
> +      else if ((version & 0xff) >= 40)
> +	version += 0x010000 - 40;
> +    }
> +
> +  return version;
> +}

… this function will have to be changed again for Linux 5.0.

A long-term solution would map the minimum required version hard-coded
into libc to its 2.6 equivalent, and check that if the kernel reports a
2.6 version.  This is solves the forward compatibility issue because the
2.6 mapping for the minimum version is known at the time glibc is compiled.

Florian
  
Aurelien Jarno Dec. 3, 2015, 8:32 p.m. UTC | #3
On 2015-12-03 10:12, Florian Weimer wrote:
> On 12/03/2015 12:22 AM, Aurelien Jarno wrote:
> > When the kernel is running under the uname26 personality (for programs
> > that cannot handle "Linux 3.0"), it maps version 3.x to 2.6.40+x and
> > 4.x to 2.6.60+x. When the GNU libc is configured with --enable-kernel=3.x
> > or 4.x and uname26 personality is enabled, binaries get abort with a
> > "FATAL: kernel too old message", even if the kernel actually supports
> > such a binary.
> 
> I'm sorry, I don't think this is the correct way to handle this.  The
> reason is that
> 
> > +/* When the kernel is running under the uname26 personality (for programs
> > +   that cannot handle "Linux 3.0"), it maps version 3.x to 2.6.40+x and
> > +   4.x to 2.6.60+x.  We need to convert that back to the standard version
> > +   numbering to be able to compare versions.  */
> > +static int
> > +convert_from_uname26 (int version)
> > +{
> > +  if ((version & 0xffff00) == 0x020600)
> > +    {
> > +      /* 2.6.40+x to 3.x */
> > +      if ((version & 0xff) >= 60)
> > +	version += 0x020000 - 60;
> > +      /* 2.6.60+x to 4.x */
> > +      else if ((version & 0xff) >= 40)
> > +	version += 0x010000 - 40;
> > +    }
> > +
> > +  return version;
> > +}
> 
> … this function will have to be changed again for Linux 5.0.
> 
> A long-term solution would map the minimum required version hard-coded
> into libc to its 2.6 equivalent, and check that if the kernel reports a
> 2.6 version.  This is solves the forward compatibility issue because the
> 2.6 mapping for the minimum version is known at the time glibc is compiled.

So you mean that we should have for example two defines in our code, one
for the standard version and the other for the 2.6 version and that both
would be defined at compilation time. Correct?

That still means we need to do the conversion at compile time to be able
to define both values.

Aurelien
  
Florian Weimer Dec. 4, 2015, 2:21 p.m. UTC | #4
On 12/03/2015 09:32 PM, Aurelien Jarno wrote:

>>> +/* When the kernel is running under the uname26 personality (for programs
>>> +   that cannot handle "Linux 3.0"), it maps version 3.x to 2.6.40+x and
>>> +   4.x to 2.6.60+x.  We need to convert that back to the standard version
>>> +   numbering to be able to compare versions.  */
>>> +static int
>>> +convert_from_uname26 (int version)
>>> +{
>>> +  if ((version & 0xffff00) == 0x020600)
>>> +    {
>>> +      /* 2.6.40+x to 3.x */
>>> +      if ((version & 0xff) >= 60)
>>> +	version += 0x020000 - 60;
>>> +      /* 2.6.60+x to 4.x */
>>> +      else if ((version & 0xff) >= 40)
>>> +	version += 0x010000 - 40;
>>> +    }
>>> +
>>> +  return version;
>>> +}
>>
>> … this function will have to be changed again for Linux 5.0.
>>
>> A long-term solution would map the minimum required version hard-coded
>> into libc to its 2.6 equivalent, and check that if the kernel reports a
>> 2.6 version.  This is solves the forward compatibility issue because the
>> 2.6 mapping for the minimum version is known at the time glibc is compiled.
> 
> So you mean that we should have for example two defines in our code, one
> for the standard version and the other for the 2.6 version and that both
> would be defined at compilation time. Correct?

Essentially, yes.

> That still means we need to do the conversion at compile time to be able
> to define both values.

Or you could write the inverse function convert_from_uname26 as an
inline function or macro, and apply it to the (constant) minimum
version.  I don't have a preference in either direction.

Florian
  
Aurelien Jarno Dec. 4, 2015, 2:43 p.m. UTC | #5
On 2015-12-04 15:21, Florian Weimer wrote:
> On 12/03/2015 09:32 PM, Aurelien Jarno wrote:
> 
> >>> +/* When the kernel is running under the uname26 personality (for programs
> >>> +   that cannot handle "Linux 3.0"), it maps version 3.x to 2.6.40+x and
> >>> +   4.x to 2.6.60+x.  We need to convert that back to the standard version
> >>> +   numbering to be able to compare versions.  */
> >>> +static int
> >>> +convert_from_uname26 (int version)
> >>> +{
> >>> +  if ((version & 0xffff00) == 0x020600)
> >>> +    {
> >>> +      /* 2.6.40+x to 3.x */
> >>> +      if ((version & 0xff) >= 60)
> >>> +	version += 0x020000 - 60;
> >>> +      /* 2.6.60+x to 4.x */
> >>> +      else if ((version & 0xff) >= 40)
> >>> +	version += 0x010000 - 40;
> >>> +    }
> >>> +
> >>> +  return version;
> >>> +}
> >>
> >> … this function will have to be changed again for Linux 5.0.
> >>
> >> A long-term solution would map the minimum required version hard-coded
> >> into libc to its 2.6 equivalent, and check that if the kernel reports a
> >> 2.6 version.  This is solves the forward compatibility issue because the
> >> 2.6 mapping for the minimum version is known at the time glibc is compiled.
> > 
> > So you mean that we should have for example two defines in our code, one
> > for the standard version and the other for the 2.6 version and that both
> > would be defined at compilation time. Correct?
> 
> Essentially, yes.
> 
> > That still means we need to do the conversion at compile time to be able
> > to define both values.
> 
> Or you could write the inverse function convert_from_uname26 as an
> inline function or macro, and apply it to the (constant) minimum
> version.  I don't have a preference in either direction.

But then this function will have to be changed again for Linux 5.0…
Which is exactly your argument against the above code.
  
Richard Henderson Dec. 4, 2015, 6:07 p.m. UTC | #6
On 12/04/2015 06:43 AM, Aurelien Jarno wrote:
> On 2015-12-04 15:21, Florian Weimer wrote:
>> On 12/03/2015 09:32 PM, Aurelien Jarno wrote:
>>
>>>>> +/* When the kernel is running under the uname26 personality (for programs
>>>>> +   that cannot handle "Linux 3.0"), it maps version 3.x to 2.6.40+x and
>>>>> +   4.x to 2.6.60+x.  We need to convert that back to the standard version
>>>>> +   numbering to be able to compare versions.  */
>>>>> +static int
>>>>> +convert_from_uname26 (int version)
>>>>> +{
>>>>> +  if ((version & 0xffff00) == 0x020600)
>>>>> +    {
>>>>> +      /* 2.6.40+x to 3.x */
>>>>> +      if ((version & 0xff) >= 60)
>>>>> +	version += 0x020000 - 60;
>>>>> +      /* 2.6.60+x to 4.x */
>>>>> +      else if ((version & 0xff) >= 40)
>>>>> +	version += 0x010000 - 40;
>>>>> +    }
>>>>> +
>>>>> +  return version;
>>>>> +}
>>>>
>>>> … this function will have to be changed again for Linux 5.0.
>>>>
>>>> A long-term solution would map the minimum required version hard-coded
>>>> into libc to its 2.6 equivalent, and check that if the kernel reports a
>>>> 2.6 version.  This is solves the forward compatibility issue because the
>>>> 2.6 mapping for the minimum version is known at the time glibc is compiled.
>>>
>>> So you mean that we should have for example two defines in our code, one
>>> for the standard version and the other for the 2.6 version and that both
>>> would be defined at compilation time. Correct?
>>
>> Essentially, yes.
>>
>>> That still means we need to do the conversion at compile time to be able
>>> to define both values.
>>
>> Or you could write the inverse function convert_from_uname26 as an
>> inline function or macro, and apply it to the (constant) minimum
>> version.  I don't have a preference in either direction.
> 
> But then this function will have to be changed again for Linux 5.0…
> Which is exactly your argument against the above code.

It doesn't make a difference either way, as far as I can see.

One presumes that no matter what the 2.6.x version numbers will be increasing.
 So if the above is not updated for 5.0, it will still produce a 4.y for y
larger than ever existed in the real version 4 line.  So the 4.y produced by
the above function will still be "newer" than any hard-coded minimum.

Similarly if one works it the other way around.


r~
  

Patch

diff --git a/ChangeLog b/ChangeLog
index c7c4c25..593cfad 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@ 
+2015-12-03  Aurelien Jarno  <aurel32@debian.org>
+
+	* sysdeps/unix/sysv/linux/dl-sysdep.c (convert_from_uname26): New.
+	(_dl_discover_osversion): Call convert_from_uname26 before returning
+	the version.
+
 2015-12-01  H.J. Lu  <hongjiu.lu@intel.com>
 
 	[BZ #19313]
diff --git a/sysdeps/unix/sysv/linux/dl-sysdep.c b/sysdeps/unix/sysv/linux/dl-sysdep.c
index a4c32f6..4fca8d1 100644
--- a/sysdeps/unix/sysv/linux/dl-sysdep.c
+++ b/sysdeps/unix/sysv/linux/dl-sysdep.c
@@ -38,6 +38,25 @@  frob_brk (void)
 # include <elf/dl-sysdep.c>
 #endif
 
+/* When the kernel is running under the uname26 personality (for programs
+   that cannot handle "Linux 3.0"), it maps version 3.x to 2.6.40+x and
+   4.x to 2.6.60+x.  We need to convert that back to the standard version
+   numbering to be able to compare versions.  */
+static int
+convert_from_uname26 (int version)
+{
+  if ((version & 0xffff00) == 0x020600)
+    {
+      /* 2.6.40+x to 3.x */
+      if ((version & 0xff) >= 60)
+	version += 0x020000 - 60;
+      /* 2.6.60+x to 4.x */
+      else if ((version & 0xff) >= 40)
+	version += 0x010000 - 40;
+    }
+
+  return version;
+}
 
 int
 attribute_hidden
@@ -65,8 +84,9 @@  _dl_discover_osversion (void)
 	    while ((ElfW(Addr)) (note + 1) - start < phdr[i].p_memsz)
 	      {
 		if (!memcmp (note, &expected_note, sizeof expected_note))
-		  return *(const ElfW(Word) *) ((const void *) note
-						+ sizeof expected_note);
+		  return convert_from_uname26(
+		    *(const ElfW(Word) *) ((const void *) note
+					   + sizeof expected_note));
 #define ROUND(len) (((len) + sizeof note->n_type - 1) & -sizeof note->n_type)
 		note = ((const void *) (note + 1)
 			+ ROUND (note->n_namesz) + ROUND (note->n_descsz));
@@ -128,5 +148,5 @@  _dl_discover_osversion (void)
   if (parts < 3)
     version <<= 8 * (3 - parts);
 
-  return version;
+  return convert_from_uname26(version);
 }