/*++ Copyright (c) Microsoft. All rights reserved. Module Name: auxv.c Abstract: This file is a test for the auxiliary vector functionality. --*/ #include #include #include #include #include #include #include #include "lxtcommon.h" #include "unittests.h" #define AUXV_UID 1004 #define AUXV_GID 1004 #define LXT_NAME "auxv" #define AUXV_TEST_SCRIPT "auxv_test_script.sh" #define AUXV_TEST_SCRIPT_SOURCE "#!" AUXV_TEST_PROGRAM_PATH #define AUXV_TEST_PROGRAM "auxv_test_program" #define AUXV_TEST_PROGRAM_PATH "/data/test/" AUXV_TEST_PROGRAM #define AUXV_TEST_PROGRAM_SOURCE_FILE "auxv_test_program.c" #define AUXV_TEST_PROGRAM_SOURCE \ "#include \n" \ "#include \n" \ "#include \n" \ "#include \n" \ "\n" \ "int main(int Argc, char** Argv)\n" \ "{\n" \ " int Index;\n" \ " char* Filename = (char*)getauxval(AT_EXECFN);\n" \ " char* Platform = (char*)getauxval(AT_PLATFORM);\n" \ " printf(\"AT_EXECFN: %%s {%%p}\\n\", Filename, Filename);\n" \ " printf(\"AT_PLATFORM: %%s {%%p}\\n\", Platform, Platform);\n" \ " for (Index = 0; Index < Argc; Index += 1) {\n" \ " printf(\"Argv[%%d] = %%s\\n\", Index, Argv[Index]);\n" \ " }\n" \ " if (Platform > Filename) {\n" \ " return -1;\n" \ " }\n" \ " if ((strcmp(Platform, \"%s\") != 0) ||\n" \ " (strcmp(Filename, \"%s\") != 0)) {\n" \ " return -1;\n" \ " }\n" \ " return 0;\n" \ "}" int AuxvAtExecfn(PLXT_ARGS Args); int AuxvGetAuxv(PLXT_ARGS Args); int AuxvGetAuxvChild(void); static const LXT_VARIATION g_LxtVariations[] = {{"getauxv", AuxvGetAuxv}, {"AT_EXECFN", AuxvAtExecfn}}; int AuxvTestEntry(int Argc, char* Argv[]) { LXT_ARGS Args; int ArgvIndex; int Result; Result = LXT_RESULT_FAILURE; // // Parse the arguments. // for (ArgvIndex = 1; ArgvIndex < Argc; ++ArgvIndex) { if (Argv[ArgvIndex][0] != '-') { printf("Unexpected character %s", Argv[ArgvIndex]); goto ErrorExit; } switch (Argv[ArgvIndex][1]) { case 'c': // // Run the getauxv child variation. // return AuxvGetAuxvChild(); // // The below arguments are taken care of by LxtInitialize. // case 'a': break; case 'v': case 'l': ++ArgvIndex; break; default: goto ErrorExit; } } // // If -c was not specified, just run the tests // LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME)); LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations))); ErrorExit: LxtUninitialize(); return 0; } int AuxvGetAuxv(PLXT_ARGS Args) { char* Argv[4]; struct stat Buffer; int ChildPid; int Mode; int OriginalMode; gid_t OriginalGid; uid_t OriginalUid; int Result; unsigned long Value; ChildPid = -1; OriginalMode = 0; OriginalUid = 0; OriginalGid = 0; Value = getauxval(AT_SECURE); LxtLogInfo("Parent AT_SECURE = %u", Value); LxtCheckEqual(Value, 0, "%u"); // change Args->Argv[0] so that it points to the new single test binary design Args->Argv[0] = Argv[0] = WSL_UNIT_TEST_BINARY; LxtLogInfo("calling stat(%s)", Args->Argv[0]); LxtCheckErrno(stat(Args->Argv[0], &Buffer)); OriginalMode = Buffer.st_mode; OriginalGid = Buffer.st_gid; OriginalUid = Buffer.st_uid; LxtLogInfo("Setting the set-user-ID bit"); Mode = OriginalMode | S_ISUID; LxtCheckErrno(chown(Args->Argv[0], AUXV_UID, AUXV_UID)); LxtCheckErrno(chmod(Args->Argv[0], Mode)); // // Start a child process to verify the value of AT_SECURE. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { Argv[1] = "auxv"; Argv[2] = "-c"; Argv[3] = NULL; execve(Args->Argv[0], Argv, NULL); LxtLogError("Execve failed, errno: %d (%s)", errno, strerror(errno)); _exit(LXT_RESULT_FAILURE); } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtLogInfo("Setting the set-group-ID bit"); Mode = OriginalMode | S_ISGID; LxtCheckErrno(chmod(Args->Argv[0], Mode)); // // Start a child process to verify the value of AT_SECURE. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { Argv[1] = "auxv"; Argv[2] = "-c"; Argv[3] = NULL; execve(Argv[0], Argv, NULL); LxtLogError("Execve failed, errno: %d (%s)", errno, strerror(errno)); _exit(LXT_RESULT_FAILURE); } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); Result = LXT_RESULT_SUCCESS; ErrorExit: if (OriginalMode != 0) { chmod(Args->Argv[0], OriginalMode); chown(Args->Argv[0], OriginalUid, OriginalGid); } if (ChildPid == 0) { _exit(Result); } return Result; } int AuxvGetAuxvChild(void) { int ChildPid; int Result; unsigned long Value; ChildPid = -1; Value = getauxval(AT_SECURE); LxtLogInfo("child AT_SECURE = %u", Value); LxtCheckEqual(Value, 1, "%u"); // // Start a child process to verify the value of AT_SECURE. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { Value = getauxval(AT_SECURE); LxtLogInfo("child fork AT_SECURE = %u", Value); LxtCheckEqual(Value, 1, "%u"); Result = LXT_RESULT_SUCCESS; goto ErrorExit; } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); Result = LXT_RESULT_SUCCESS; ErrorExit: if (ChildPid == 0) { _exit(Result); } return Result; } int AuxvAtExecfnCompile(char* Filename) { char* Argv[5]; char Buffer[1024]; int ByteCount; int ChildPid; int Fd; char* Platform; int Result; ChildPid = -1; Fd = -1; Platform = (char*)getauxval(AT_PLATFORM); ByteCount = snprintf(Buffer, sizeof(Buffer), AUXV_TEST_PROGRAM_SOURCE, Platform, Filename); if (ByteCount < 0) { Result = LXT_RESULT_FAILURE; LxtLogError("Formatting test source failed %d", errno); goto ErrorExit; } // // Create the source file to be compiled. // LxtCheckErrno(Fd = creat(AUXV_TEST_PROGRAM_SOURCE_FILE, 0755)); LxtCheckErrno(ByteCount = write(Fd, Buffer, ByteCount)); // // Compile the binary // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { Filename = "/usr/bin/gcc"; Argv[0] = "gcc"; Argv[1] = AUXV_TEST_PROGRAM_SOURCE_FILE; Argv[2] = "-o"; Argv[3] = AUXV_TEST_PROGRAM_PATH; Argv[4] = NULL; LxtCheckErrno(execv(Filename, Argv)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPollOptions(ChildPid, LXT_RESULT_SUCCESS, 0, 30)); Result = LXT_RESULT_SUCCESS; ErrorExit: if (Fd != -1) { LxtClose(Fd); } if (ChildPid == 0) { _exit(Result); } unlink(AUXV_TEST_PROGRAM_SOURCE_FILE); return Result; } int AuxvAtExecfn(PLXT_ARGS Args) { char* Argv[2]; int ByteCount; int ChildPid; char* Environment[2]; int Fd; char* Filename; int Result; ChildPid = -1; Fd = -1; Filename = AUXV_TEST_PROGRAM_PATH; LxtCheckResult(AuxvAtExecfnCompile(Filename)); // // Run the binary with a non-null argument array. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { Argv[0] = AUXV_TEST_PROGRAM_PATH; Argv[1] = NULL; LxtCheckErrno(execv(Filename, Argv)); goto ErrorExit; } LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Run the binary with a null argument array. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { Environment[0] = "FOO=bar"; Environment[1] = NULL; LxtCheckErrno(LxtExecve(Filename, NULL, Environment)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Run the binary with an Argv[0] that does not match the filename. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { Argv[0] = "FOO"; Argv[1] = NULL; LxtCheckErrno(execv(Filename, Argv)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Run the binary an empty command line. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { Environment[0] = "FOO=bar"; Environment[1] = NULL; LxtCheckErrno(LxtExecve(Filename, NULL, Environment)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Run the binary with null argument and environment arrays. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(LxtExecve(Filename, NULL, NULL)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Create a script that uses #! to launch the test binary. // LxtCheckErrno(Fd = creat(AUXV_TEST_SCRIPT, 0755)); LxtCheckErrno(ByteCount = write(Fd, AUXV_TEST_SCRIPT_SOURCE, sizeof(AUXV_TEST_SCRIPT_SOURCE))); LxtClose(Fd); Fd = -1; // // Recompile the binary with the script as the expected AT_EXECFN. // Filename = AUXV_TEST_SCRIPT; LxtCheckResult(AuxvAtExecfnCompile(Filename)); // // Run the script with a non-null argument array. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { Argv[0] = Filename; Argv[1] = NULL; LxtCheckErrno(execv(Filename, Argv)); goto ErrorExit; } LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Run the script with a null argument array. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { Environment[0] = "FOO=bar"; Environment[1] = NULL; LxtCheckErrno(LxtExecve(Filename, NULL, Environment)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Run the script with an Argv[0] that does not match the filename. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { Argv[0] = "FOO"; Argv[1] = NULL; LxtCheckErrno(execv(Filename, Argv)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Run the script an empty command line. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { Environment[0] = "FOO=bar"; Environment[1] = NULL; LxtCheckErrno(LxtExecve(Filename, NULL, Environment)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Run the script with null argument and environment arrays. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(LxtExecve(Filename, NULL, NULL)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); Result = LXT_RESULT_SUCCESS; ErrorExit: if (Fd != -1) { LxtClose(Fd); } if (ChildPid == 0) { _exit(Result); } unlink(AUXV_TEST_PROGRAM_PATH); unlink(AUXV_TEST_SCRIPT); return Result; }