haskell / unix

POSIX functionality
https://hackage.haskell.org/package/unix
Other
107 stars 92 forks source link

getAllGroupEntries fail on rhel8 #292

Closed rickeyski closed 11 months ago

rickeyski commented 11 months ago

Thanks for your attention. Please let me know if I can provide any further information.

Rickey

Your environment

Which OS do you use:

$ cat /etc/os-release 
NAME="Red Hat Enterprise Linux"
VERSION="8.7 (Ootpa)"
ID="rhel"
ID_LIKE="fedora"
VERSION_ID="8.7"
PLATFORM_ID="platform:el8"
PRETTY_NAME="Red Hat Enterprise Linux 8.7 (Ootpa)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:redhat:enterprise_linux:8::baseos"
HOME_URL="https://www.redhat.com/"
DOCUMENTATION_URL="https://access.redhat.com/documentation/red_hat_enterprise_linux/8/"
BUG_REPORT_URL="https://bugzilla.redhat.com/"

REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 8"
REDHAT_BUGZILLA_PRODUCT_VERSION=8.7
REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux"
REDHAT_SUPPORT_PRODUCT_VERSION="8.7"

Describe your project (alternative: link to the project): I made a reproducing sample project here It fails with both stackage snapshots in the project lts-20.24 and lts-21.9

Steps to reproduce

Run the reproducer project

$ stack run

Expected behaviour

List all groupIds for user On Rhel 7.9, Amzn Linux 2, and Ubuntu 22.04 it works correctly

Actual behaviour

(unix-test-exe: getAllGroupEntries: invalid argument (Invalid argument))

Include debug information

~/.stack/programs/x86_64-linux/ghc-tinfo6-libc6-pre232-9.4.6/lib/ghc-9.4.6/lib/x86_64-linux-ghc-9.4.6/unix-2.7.3/include/HsUnixConfig.h
/* include/HsUnixConfig.h.  Generated from HsUnixConfig.h.in by configure.  */
/* include/HsUnixConfig.h.in.  Generated from configure.ac by autoheader.  */

/* The value of SIGABRT. */
#define CONST_SIGABRT 6

/* The value of SIGALRM. */
#define CONST_SIGALRM 14

/* The value of SIGBUS. */
#define CONST_SIGBUS 7

/* The value of SIGCHLD. */
#define CONST_SIGCHLD 17

/* The value of SIGCONT. */
#define CONST_SIGCONT 18

/* The value of SIGFPE. */
#define CONST_SIGFPE 8

/* The value of SIGHUP. */
#define CONST_SIGHUP 1

/* The value of SIGILL. */
#define CONST_SIGILL 4

/* The value of SIGINFO. */
#define CONST_SIGINFO -1

/* The value of SIGINT. */
#define CONST_SIGINT 2

/* The value of SIGKILL. */
#define CONST_SIGKILL 9

/* The value of SIGPIPE. */
#define CONST_SIGPIPE 13

/* The value of SIGPOLL. */
#define CONST_SIGPOLL 29

/* The value of SIGPROF. */
#define CONST_SIGPROF 27

/* The value of SIGQUIT. */
#define CONST_SIGQUIT 3

/* The value of SIGSEGV. */
#define CONST_SIGSEGV 11

/* The value of SIGSTOP. */
#define CONST_SIGSTOP 19

/* The value of SIGSYS. */
#define CONST_SIGSYS 31

/* The value of SIGTERM. */
#define CONST_SIGTERM 15

/* The value of SIGTRAP. */
#define CONST_SIGTRAP 5

/* The value of SIGTSTP. */
#define CONST_SIGTSTP 20

/* The value of SIGTTIN. */
#define CONST_SIGTTIN 21

/* The value of SIGTTOU. */
#define CONST_SIGTTOU 22

/* The value of SIGURG. */
#define CONST_SIGURG 23

/* The value of SIGUSR1. */
#define CONST_SIGUSR1 10

/* The value of SIGUSR2. */
#define CONST_SIGUSR2 12

/* The value of SIGVTALRM. */
#define CONST_SIGVTALRM 26

/* The value of SIGWINCH. */
#define CONST_SIGWINCH 28

/* The value of SIGXCPU. */
#define CONST_SIGXCPU 24

/* The value of SIGXFSZ. */
#define CONST_SIGXFSZ 25

/* The value of SIG_BLOCK. */
#define CONST_SIG_BLOCK 0

/* The value of SIG_SETMASK. */
#define CONST_SIG_SETMASK 2

/* The value of SIG_UNBLOCK. */
#define CONST_SIG_UNBLOCK 1

/* Define to 1 if you have the <bsd/libutil.h> header file. */
/* #undef HAVE_BSD_LIBUTIL_H */

/* Define to 1 if you have the `clearenv' function. */
#define HAVE_CLEARENV 1

/* Define to 1 if you have the `ctermid' function. */
#define HAVE_CTERMID 1

/* Define to 1 if you have the declaration of `ctermid', and to 0 if you
   don't. */
#define HAVE_DECL_CTERMID 1

/* Define to 1 if you have the declaration of `execvpe', and to 0 if you
   don't. */
#define HAVE_DECL_EXECVPE 1

/* Define to 1 if you have the declaration of `fdatasync', and to 0 if you
   don't. */
#define HAVE_DECL_FDATASYNC 1

/* Define to 1 if you have the declaration of `tcdrain', and to 0 if you
   don't. */
#define HAVE_DECL_TCDRAIN 1

/* Define if we have /dev/ptc. */
/* #undef HAVE_DEV_PTC */

/* Define if we have /dev/ptmx. */
#define HAVE_DEV_PTMX 1

/* Define to 1 if you have the <dirent.h> header file. */
#define HAVE_DIRENT_H 1

/* Define to 1 if you have the `execvpe' function. */
#define HAVE_EXECVPE 1

/* Define to 1 if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1

/* Define to 1 if you have the `fdatasync' function. */
#define HAVE_FDATASYNC 1

/* Define to 1 if you have the `fsync' function. */
#define HAVE_FSYNC 1

/* Define to 1 if you have the `futimens' function. */
#define HAVE_FUTIMENS 1

/* Define to 1 if you have the `futimes' function. */
#define HAVE_FUTIMES 1

/* Define to 1 if you have the `getgrent' function. */
#define HAVE_GETGRENT 1

/* Define to 1 if you have the `getgrgid_r' function. */
#define HAVE_GETGRGID_R 1

/* Define to 1 if you have the `getgrnam_r' function. */
#define HAVE_GETGRNAM_R 1

/* Define to 1 if you have the `getpwent' function. */
#define HAVE_GETPWENT 1

/* Define to 1 if you have the `getpwnam' function. */
#define HAVE_GETPWNAM 1

/* Define to 1 if you have the `getpwnam_r' function. */
#define HAVE_GETPWNAM_R 1

/* Define to 1 if you have the `getpwuid' function. */
#define HAVE_GETPWUID 1

/* Define to 1 if you have the `getpwuid_r' function. */
#define HAVE_GETPWUID_R 1

/* Define to 1 if you have the <grp.h> header file. */
#define HAVE_GRP_H 1

/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1

/* Define to 1 if you have the `lchown' function. */
#define HAVE_LCHOWN 1

/* Define to 1 if you have the <libutil.h> header file. */
/* #undef HAVE_LIBUTIL_H */

/* Define to 1 if you have the <limits.h> header file. */
#define HAVE_LIMITS_H 1

/* Define to 1 if you have the `lutimes' function. */
#define HAVE_LUTIMES 1

/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1

/* Define to 1 if you have the `mkdtemp' function. */
#define HAVE_MKDTEMP 1

/* Define to 1 if you have the `mkstemps' function. */
#define HAVE_MKSTEMPS 1

/* Define to 1 if you have the `nanosleep' function. */
#define HAVE_NANOSLEEP 1

/* Ignore the pw_gecos member of passwd where it does not exist */
/* #undef HAVE_NO_PASSWD_PW_GECOS */

/* Define to 1 if you have the `openpty' function. */
#define HAVE_OPENPTY 1

/* Define to 1 if you have the `posix_fadvise' function. */
#define HAVE_POSIX_FADVISE 1

/* Define to 1 if you have the `posix_fallocate' function. */
#define HAVE_POSIX_FALLOCATE 1

/* Define to 1 if you have the `ptsname' function. */
#define HAVE_PTSNAME 1

/* Define to 1 if you have the <pty.h> header file. */
#define HAVE_PTY_H 1

/* Define to 1 if you have the <pwd.h> header file. */
#define HAVE_PWD_H 1

/* Define to 1 if you have the `readdir_r' function. */
#define HAVE_READDIR_R 1

/* Define to 1 if RTLD_DEFAULT is available. */
#define HAVE_RTLDDEFAULT 1

/* Define to 1 if we can see RTLD_NEXT in dlfcn.h. */
#define HAVE_RTLDNEXT 1

/* Define to 1 if <unistd.h> defines _SC_GETGR_R_SIZE_MAX. */
#define HAVE_SC_GETGR_R_SIZE_MAX 1

/* Define to 1 if <unistd.h> defines _SC_GETPW_R_SIZE_MAX. */
#define HAVE_SC_GETPW_R_SIZE_MAX 1

/* Define to 1 if you have the `seekdir' function. */
#define HAVE_SEEKDIR 1

/* Define to 1 if you have the `setenv' function. */
#define HAVE_SETENV 1

/* Define to 1 if you have the `setitimer' function. */
#define HAVE_SETITIMER 1

/* Define to 1 if you have the `shm_open' function. */
#define HAVE_SHM_OPEN 1

/* Define to 1 if you have the `shm_unlink' function. */
#define HAVE_SHM_UNLINK 1

/* Define to 1 if you have the <signal.h> header file. */
#define HAVE_SIGNAL_H 1

/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1

/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1

/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1

/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1

/* Define to 1 if `st_atim' is a member of `struct stat'. */
#define HAVE_STRUCT_STAT_ST_ATIM 1

/* Define to 1 if `st_atimensec' is a member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_ATIMENSEC */

/* Define to 1 if `st_atimespec' is a member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_ATIMESPEC */

/* Define to 1 if `st_atime_n' is a member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_ATIME_N */

/* Define to 1 if `st_ctim' is a member of `struct stat'. */
#define HAVE_STRUCT_STAT_ST_CTIM 1

/* Define to 1 if `st_ctimensec' is a member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_CTIMENSEC */

/* Define to 1 if `st_ctimespec' is a member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_CTIMESPEC */

/* Define to 1 if `st_ctime_n' is a member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_CTIME_N */

/* Define to 1 if `st_mtim' is a member of `struct stat'. */
#define HAVE_STRUCT_STAT_ST_MTIM 1

/* Define to 1 if `st_mtimensec' is a member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_MTIMENSEC */

/* Define to 1 if `st_mtimespec' is a member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_MTIMESPEC */

/* Define to 1 if `st_mtime_n' is a member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_MTIME_N */

/* Define to 1 if `st_uatime' is a member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_UATIME */

/* Define to 1 if `st_uctime' is a member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_UCTIME */

/* Define to 1 if `st_umtime' is a member of `struct stat'. */
/* #undef HAVE_STRUCT_STAT_ST_UMTIME */

/* Define to 1 if you have the `sysconf' function. */
#define HAVE_SYSCONF 1

/* Define to 1 if you have the <sys/resource.h> header file. */
#define HAVE_SYS_RESOURCE_H 1

/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1

/* Define to 1 if you have the <sys/times.h> header file. */
#define HAVE_SYS_TIMES_H 1

/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1

/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1

/* Define to 1 if you have the <sys/utsname.h> header file. */
#define HAVE_SYS_UTSNAME_H 1

/* Define to 1 if you have the <sys/wait.h> header file. */
#define HAVE_SYS_WAIT_H 1

/* Define to 1 if you have the `tcdrain' function. */
#define HAVE_TCDRAIN 1

/* Define to 1 if you have the `telldir' function. */
#define HAVE_TELLDIR 1

/* Define to 1 if you have the <termios.h> header file. */
#define HAVE_TERMIOS_H 1

/* Define to 1 if you have the <time.h> header file. */
#define HAVE_TIME_H 1

/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1

/* Define to 1 if you have the `unsetenv' function. */
#define HAVE_UNSETENV 1

/* Define to 1 if you have the `utimensat' function. */
#define HAVE_UTIMENSAT 1

/* Define to 1 if you have the <utime.h> header file. */
#define HAVE_UTIME_H 1

/* Define to 1 if you have the <utmp.h> header file. */
#define HAVE_UTMP_H 1

/* Define to 1 if you have the `_NSGetEnviron' function. */
/* #undef HAVE__NSGETENVIRON */

/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "libraries@haskell.org"

/* Define to the full name of this package. */
#define PACKAGE_NAME "Haskell unix package"

/* Define to the full name and version of this package. */
#define PACKAGE_STRING "Haskell unix package 2.0"

/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "unix"

/* Define to the home page for this package. */
#define PACKAGE_URL ""

/* Define to the version of this package. */
#define PACKAGE_VERSION "2.0"

/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1

/* Define if stdlib.h declares unsetenv to return void. */
/* #undef UNSETENV_RETURNS_VOID */

/* Enable extensions on AIX 3, Interix.  */
#ifndef _ALL_SOURCE
# define _ALL_SOURCE 1
#endif
/* Enable GNU extensions on systems that have them.  */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
/* Enable threading extensions on Solaris.  */
#ifndef _POSIX_PTHREAD_SEMANTICS
# define _POSIX_PTHREAD_SEMANTICS 1
#endif
/* Enable extensions on HP NonStop.  */
#ifndef _TANDEM_SOURCE
# define _TANDEM_SOURCE 1
#endif
/* Enable general extensions on Solaris.  */
#ifndef __EXTENSIONS__
# define __EXTENSIONS__ 1
#endif

/* Define if the system headers declare usleep to return void. */
/* #undef USLEEP_RETURNS_VOID */

/* Enable large inode numbers on Mac OS X 10.5.  */
#ifndef _DARWIN_USE_64_BIT_INODE
# define _DARWIN_USE_64_BIT_INODE 1
#endif

/* Number of bits in a file offset, on hosts where this is settable. */
/* #undef _FILE_OFFSET_BITS */

/* Define for large files, on AIX-style hosts. */
/* #undef _LARGE_FILES */

/* Define to 1 if on MINIX. */
/* #undef _MINIX */

/* Define to 2 if the system does not provide POSIX.1 features except with
   this defined. */
/* #undef _POSIX_1_SOURCE */

/* Define to 1 if you need to in order for `stat' and other things to work. */
/* #undef _POSIX_SOURCE */

/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
rickeyski commented 11 months ago

Additionally, I wrote a C program to test getgrent directly and it succeeds, providing the same output on rhel 7 as rhel 8.

#include <stdio.h>
#include <grp.h>

int main(int argc, char** argv){
    struct group *tmpGrp;

    while((tmpGrp = getgrent()) != NULL) {
        printf("group : %s\n", tmpGrp->gr_name);
    }
    printf("\n");
    return 0;
}
hasufell commented 11 months ago

Tried on rocky8 (docker container), which should be similar to rhel8.

I could not reproduce with cabal and the following GHCs:

These are the fedora27 bindists.

I also tried with ghc-9.4.6-x86_64-centos7-linux.tar.xz just in case. It still succeeds. I don't have a proper rhel8 machine, but rocky8 should be binary compatible/equivalent.

hasufell commented 11 months ago

@juhp

rickeyski commented 11 months ago

So far the only difference I have found between the rocky linux version and rhel8 is the build number of glibc

glibc-2.28-164.el8.x86_64 -- rhel8
glibc-2.28-225.el8.x86_64 -- rocky 8
juhp commented 11 months ago

(Sorry but next time please remove unnecessary deps like pretty-simple from your reproducer - it is not needed right?) (Would be nice if the reproducer used the current user too:-)

I don't have a fresh rhel8 release vm right now. I tried on a current Centos Stream 8 container (with stack):

$ podman run -it --rm quay.io/centos/centos:stream8
:
[root@9648a641a1ab test-unix]# stack install
[root@9648a641a1ab test-unix]# ~/.local/bin/test-unix-exe 
[]
[root@9648a641a1ab test-unix]# rpm -q glibc
glibc-2.28-236.el8.x86_64
[root@9648a641a1ab test-unix]# ./a.out | wc -l
36
hasufell commented 11 months ago

Can you reproduce with cabal and ghcup?

juhp commented 11 months ago

Actually not seeing any errors at all, so I can't reproduce

I guess runghc test.sh would be sufficient with

-- test.hs
import System.Posix.Types
import System.Posix.User

main = do
  user <- getLoginName
  getAllGroupEntries >>= print . userGIDList user
  where
    userGIDList :: String -> [GroupEntry] -> [GroupID]
    userGIDList user groups = map groupID $ filter (elem user . groupMembers) groups

Wondering if something wrong with your system, @rickeyski

It sounds like getAllGroupEntries is erroring for you. If so all you need to do to reproduce is

$ ghci
> import System.Posix.User
> getAllGroupEntries
juhp commented 11 months ago

Or even simpler than the runghc above:

$ ghci
> import System.Posix.User
> import Control.Monad
> printMembers g = let mems = groupMembers g in unless (null mems) $ putStrLn $ groupName g ++ ": " ++ unwords mems
> getAllGroupEntries >>= mapM_ printMembers
wheel: user1
kvm: qemu
tss: clevis
juhp commented 11 months ago

Anyway to conclude: I can neither reproduce your error in a Centos Stream 8 container nor a RHEL8 VM. Since noone else is reporting this (AFAIK?) it seems unlikely to be a general problem.

rickeyski commented 11 months ago

Interesting. Thanks for the quick response. I will dig further into my system since it seems to be that build on ec2. To be clear, the simple reproducer was correct.

$ ghci
> import System.Posix.User
>g <- getAllGroupEntries
*** Exception: getAllGroupEntries: invalid argument (Invalid argument)

Next time I will trim more away. Thanks again for your help.

juhp commented 11 months ago

You don't need to close though, it could well be a genuine issue with AWS likely then.

juhp commented 11 months ago

Could you also try with the ghc in EPEL 8?

juhp commented 11 months ago

The definition is here I think.

You could also try System.Posix.User.ByteString.getAllGroupEntries perhaps just to compare. (this is only in 9.6) (But you could try unix-2.8 to compare perhaps if it builds with older ghc.)

rickeyski commented 11 months ago

Could you also try with the ghc in EPEL 8?

What do you mean? The system ghc?

juhp commented 11 months ago

Yes exactly (from Fedora EPEL) it is much older though: ghc 8.2.

rickeyski commented 11 months ago

Same issue with epel install packages

$ ghci
GHCi, version 8.2.2: http://www.haskell.org/ghc/  :? for help
Prelude> import System.Posix.User
Prelude System.Posix.User> g <- getAllGroupEntries 
*** Exception: getAllGroupEntries: invalid argument (Invalid argument)
juhp commented 11 months ago

You could also try ghc-9.6.2 and/or latest unix-2.8.1.1, although probably unlikely it helps, but nevertheless a good idea before reporting this to upstream ghc at least.

I commented https://github.com/rickeyski/test-unix/issues/1#issuecomment-1699305176

juhp commented 11 months ago

(Also curious what the actual problem is you are trying to solve with this (maybe there is a workaround/alternative?), but we should get to the bottom of this issue and understand it properly.)

juhp commented 11 months ago

Also you could try with RHEL 9 for good measure if you wish. Just trying to understanding the affected system space until we can dig deeper in the core issue.

rickeyski commented 11 months ago

Tried with RHEL 9.2 glibc-2.34-60 ghc 9.4, 9.2, and 8.10 all succeeded as expected...

I am working on a app that uses the group lookup as part of a saml auth flow. We just started moving from RHEL 7 to RHEL 8 and discovered this issue. I am thinking of pivoting to just creating a static build linked against musl to avoid this problem.

rickeyski commented 11 months ago

Not sure why I didn't think to try this earlier, but the host had a pending glibc update. Updating to glibc-2.28-225.el8.x86_64 has fixed the issue. I need to dive into what happened with 2.28-164.

juhp commented 11 months ago

Okay that is indeed surprising, thanks for testing. I guess we can close this unless you still see an issue. ((also just my 2c but at this point I feel it is worth considering using RHEL 9 instead but anyway - also EPEL 9 has all recent ghc versions in ghcX.Y btw))

rickeyski commented 11 months ago

Sounds good. I wish we could go right to RHEL9 given how much of a mess it has been to move to RHEL8, but that is above my paygrade. Thanks again for your help.