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 1 year ago

rickeyski commented 1 year 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 1 year 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 1 year 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 1 year ago

@juhp

rickeyski commented 1 year 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 1 year 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 1 year ago

Can you reproduce with cabal and ghcup?

juhp commented 1 year 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 1 year 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 1 year 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 1 year 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 1 year ago

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

juhp commented 1 year ago

Could you also try with the ghc in EPEL 8?

juhp commented 1 year 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 1 year ago

Could you also try with the ghc in EPEL 8?

What do you mean? The system ghc?

juhp commented 1 year ago

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

rickeyski commented 1 year 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 1 year 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 1 year 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 1 year 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 1 year 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 1 year 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 1 year 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 1 year 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.