Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #3276: Remove two major defects in posixlib utsname.scala #3280

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/changelog/0.5.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,20 @@ Zone { implicit z =>
val atimensec = stat.st_atim.tv_nsec
}


```

### Corrections to POSIX sys/utsname.scala

A number of defects have been corrected in `sys/utsname.scala`. These
corrections required breaking changes to field definition. The change
most noticeable to end users is likely to be that the `uname` object,
holding implicit conversions, has been renamed to `utsname`.

A Test in `UtsnameTest.scala` shows on way of using the required CArray
fields in the `utsname` structure as instances of Scala types.


## Deprecated definitions

### Removed in this version
Expand Down
58 changes: 41 additions & 17 deletions posixlib/src/main/resources/scala-native/sys/uname.c
Original file line number Diff line number Diff line change
@@ -1,34 +1,58 @@
#if defined(__unix__) || defined(__unix) || defined(unix) || \
(defined(__APPLE__) && defined(__MACH__))
#include <sys/utsname.h>
#include <stddef.h>
#include <string.h>

#define NAMELEN 256
#ifdef SCALANATIVE_UTSNAMELEN
#error "Conflicting prior definition of SCALANATIVE_UTSNAMELEN"
#endif

// For origin of the 256 "magic" number, see comments in utsname.scala
#define SCALANATIVE_UTSNAMELEN 256

struct scalanative_utsname {
char sysname[NAMELEN];
char nodename[NAMELEN];
char release[NAMELEN];
char version[NAMELEN];
char machine[NAMELEN];
char sysname[SCALANATIVE_UTSNAMELEN];
char nodename[SCALANATIVE_UTSNAMELEN];
char release[SCALANATIVE_UTSNAMELEN];
char version[SCALANATIVE_UTSNAMELEN];
char machine[SCALANATIVE_UTSNAMELEN];
};
#undef NAMELEN
#define SET_FIELD(x, y) \
do { \
int len = strlen(y); \
memcpy(x, y, len); \
} while (0);

#define SIZEOF_FIELD(t, f) (sizeof(((t *)0)->f))

_Static_assert(SIZEOF_FIELD(struct utsname, sysname) <= SCALANATIVE_UTSNAMELEN,
"Unexpected size: OS utsname.sysname");

_Static_assert(SIZEOF_FIELD(struct utsname, nodename) <= SCALANATIVE_UTSNAMELEN,
"Unexpected size: OS utsname.nodename");

_Static_assert(SIZEOF_FIELD(struct utsname, release) <= SCALANATIVE_UTSNAMELEN,
"Unexpected size: OS utsname.release");

_Static_assert(SIZEOF_FIELD(struct utsname, version) <= SCALANATIVE_UTSNAMELEN,
"Unexpected size: OS utsname.version");

_Static_assert(SIZEOF_FIELD(struct utsname, machine) <= SCALANATIVE_UTSNAMELEN,
"Unexpected size: OS utsname.machine");

#define SET_FIELD(dst, src) memccpy(dst, src, 0, SCALANATIVE_UTSNAMELEN)

int scalanative_uname(struct scalanative_utsname *scalanative_utsname) {
struct utsname utsname;
int res = uname(&utsname);
if (res == 0) {
SET_FIELD(&scalanative_utsname->sysname, utsname.sysname)
SET_FIELD(&scalanative_utsname->nodename, utsname.nodename)
SET_FIELD(&scalanative_utsname->release, utsname.release)
SET_FIELD(&scalanative_utsname->version, utsname.version)
SET_FIELD(&scalanative_utsname->machine, utsname.machine)
SET_FIELD(&scalanative_utsname->sysname, utsname.sysname);
SET_FIELD(&scalanative_utsname->nodename, utsname.nodename);
SET_FIELD(&scalanative_utsname->release, utsname.release);
SET_FIELD(&scalanative_utsname->version, utsname.version);
SET_FIELD(&scalanative_utsname->machine, utsname.machine);
}
return res;
}

#undef SCALANATIVE_UTSNAMELEN
#undef SET_FIELD
#undef SIZEOF_FIELD

#endif // Unix or Mac OS
28 changes: 26 additions & 2 deletions posixlib/src/main/scala/scala/scalanative/posix/sys/utsname.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,38 @@ import scala.scalanative.unsafe.Nat._

@extern
object utsname {
/* Design notes:
* 1) The 256 "magic"" number appears to be the macOS macro _SYS_NAMELEN.
* Linux uses a much smaller number (65).
* _Static_assert() guard code exists in uname.c to ensure that
* the size used by the operating system is less than or equal to this.
* That prevents new or changed operating systems from writing to
* memory where it should: i.e. spraying memory.
*
* 2) The allocation of the entire array inside the structure follows
* the Open Group 2018 POSIX description. That is, the fields are
* actual arrays (CArray) and not the pointers to the beginning of an
* array (Ptr[Byte]) one might expect.
*
* 3) The CArrays are somewhat difficult to work with in Scala.
* The operating system will have placed a null
* somewhere in the CArray provided to it. Given that, proper
* Scala Strings can be obtained by:
* import scala.scalanative.unsafe._
* fromCString(u.sysname.at(0).asInstanceOf[CString])
*/

// If changes are made here, corresponding changes in uname.c may be needed.
type _256 = Digit3[_2, _5, _6]
private type str = CArray[Byte, _256]
type utsname = CStruct5[str, str, str, str, str]

@name("scalanative_uname")
@extern def uname(utsname: Ptr[utsname]): CInt = extern
}

object uname {
implicit class utsnameOps(val c: Ptr[utsname.utsname]) {
object utsnameOps {
implicit class utsnamePtrOps(val c: Ptr[utsname.utsname]) {
def sysname = c._1
def nodename = c._2
def release = c._3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package org.scalanative.testsuite.posixlib.sys

import scalanative.unsafe._
import scala.scalanative.meta.LinktimeInfo.isWindows
import scala.scalanative.posix.sys.uname._
import scala.scalanative.posix.sys.utsname._
import scala.scalanative.posix.sys.utsnameOps._
import scala.scalanative.unsafe._

import org.junit.Test
import org.junit.Assert._
import org.junit.Ignore

class UtsnameTest {
@Test def utsnameOpsTest(): Unit = if (!isWindows) {
Expand Down Expand Up @@ -58,4 +59,33 @@ class UtsnameTest {
)

}

/* This is a visual test, hence ignored in CI.
* It increases confidence in the results of the uname() call by
* allowing developers to validate results in known environments.
* CI has too many possibly valid values for this writer to automate.
*/
@Ignore
@Test def unameVisual(): Unit = if (!isWindows) {

val u: Ptr[utsname] = stackalloc[utsname]()

val r = uname(u)

assertEquals(s"uname failed, result is ${r}", r, 0)

val sysname = fromCString(u.sysname.at(0).asInstanceOf[CString])
val nodename = fromCString(u.nodename.at(0).asInstanceOf[CString])
val release = fromCString(u.release.at(0).asInstanceOf[CString])
val version = fromCString(u.version.at(0).asInstanceOf[CString])
val machine = fromCString(u.machine.at(0).asInstanceOf[CString])

printf(s"\n\n")
printf(s"uname.sysname: '${sysname}'\n")
printf(s"uname.nodename: '${nodename}'\n")
printf(s"uname.release: '${release}'\n")
printf(s"uname.version: '${version}'\n")
printf(s"uname.machine: '${machine}'\n")
printf(s"\n\n")
}
}