mirror of
https://codeberg.org/la-chouette/minishell.git
synced 2025-12-06 07:28:09 +01:00
fix(get_command_path): leak when path unset and calling file present in current dir
$ touch cat $ unset PATH $ cat m==75845== Memcheck, a memory error detector ==75845== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al. ==75845== Using Valgrind-3.23.0 and LibVEX; rerun with -h for copyright info ==75845== Command: ./minishell ==75845== ==75846== Memcheck, a memory error detector ==75846== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al. ==75846== Using Valgrind-3.23.0 and LibVEX; rerun with -h for copyright info ==75846== Command: /nix/store/6wgd8c9vq93mqxzc7jhkl86mv6qbc360-coreutils-9.5/bin/touch cat ==75846== ==75846== ==75846== FILE DESCRIPTORS: 0 open (0 std) at exit. ==75846== ==75846== HEAP SUMMARY: ==75846== in use at exit: 0 bytes in 0 blocks ==75846== total heap usage: 46 allocs, 46 frees, 8,126 bytes allocated ==75846== ==75846== All heap blocks were freed -- no leaks are possible ==75846== ==75846== For lists of detected and suppressed errors, rerun with: -s ==75846== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) minishell: cat: Permission denied ==75849== ==75849== FILE DESCRIPTORS: 3 open (3 std) at exit. ==75849== ==75849== HEAP SUMMARY: ==75849== in use at exit: 4,181 bytes in 12 blocks ==75849== total heap usage: 982 allocs, 970 frees, 122,299 bytes allocated ==75849== ==75849== 8 bytes in 1 blocks are definitely lost in loss record 1 of 9 ==75849== at 0x48467D9: malloc (in /nix/store/phbnjdfmy3v4ak9xf211y2336mv5kx9s-valgrind-3.23.0/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==75849== by 0x113C22: ft_calloc (ft_calloc.c:28) ==75849== by 0x112399: get_paths_array (src/subst/simple_filename_exp.c:37) ==75849== by 0x1121EE: filepath_from_env (src/subst/simple_filename_exp.c:144) ==75849== by 0x1121C0: get_cmdpath (src/subst/simple_filename_exp.c:184) ==75849== by 0x10CD97: exec_external_cmd (src/executing/simple_cmd/simple_cmd_execute.c:67) ==75849== by 0x10CC58: simple_cmd_execute (src/executing/simple_cmd/simple_cmd_execute.c:94) ==75849== by 0x10B12C: cmd_execute (src/executing/cmd/cmd_execute.c:23) ==75849== by 0x10A4FD: execute_command (src/minishell.c:37) ==75849== by 0x10A3B2: main (src/minishell.c:90) ==75849== ==75849== 24 bytes in 1 blocks are still reachable in loss record 6 of 9 ==75849== at 0x48467D9: malloc (in /nix/store/phbnjdfmy3v4ak9xf211y2336mv5kx9s-valgrind-3.23.0/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==75849== by 0x113C22: ft_calloc (ft_calloc.c:28) ==75849== by 0x10D4A9: cmd_create (src/parser/cmd/cmd.c:20) ==75849== by 0x10F6A1: simple_cmd_create (src/parser/simple_cmd/simple_cmd_parse.c:24) ==75849== by 0x10F4E4: minishell_simple_cmd_parse (src/parser/simple_cmd/simple_cmd_parse.c:38) ==75849== by 0x10E078: minishell_group_or_simple_parse (src/parser/group_cmd/group_cmd_parse.c:53) ==75849== by 0x10E4AC: minishell_pipeline_parse (src/parser/pipeline/pipeline_parse.c:26) ==75849== by 0x10D93C: minishell_cmds_parse (src/parser/cmd/cmds_parse.c:30) ==75849== by 0x1139D5: minishell_parse (src/parser/cmd_parsing.c:67) ==75849== by 0x10A38B: main (src/minishell.c:87) ==75849== ==75849== LEAK SUMMARY: ==75849== definitely lost: 8 bytes in 1 blocks ==75849== indirectly lost: 0 bytes in 0 blocks ==75849== possibly lost: 0 bytes in 0 blocks ==75849== still reachable: 24 bytes in 1 blocks ==75849== suppressed: 4,149 bytes in 10 blocks ==75849== ==75849== For lists of detected and suppressed errors, rerun with: -s ==75849== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) 126 ==75845== ==75845== FILE DESCRIPTORS: 3 open (3 std) at exit. ==75845== ==75845== HEAP SUMMARY: ==75845== in use at exit: 4,201 bytes in 14 blocks ==75845== total heap usage: 844 allocs, 830 frees, 72,248 bytes allocated ==75845== ==75845== 8 bytes in 1 blocks are definitely lost in loss record 1 of 8 ==75845== at 0x48467D9: malloc (in /nix/store/phbnjdfmy3v4ak9xf211y2336mv5kx9s-valgrind-3.23.0/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==75845== by 0x113C22: ft_calloc (ft_calloc.c:28) ==75845== by 0x112399: get_paths_array (src/subst/simple_filename_exp.c:37) ==75845== by 0x1121EE: filepath_from_env (src/subst/simple_filename_exp.c:144) ==75845== by 0x1121C0: get_cmdpath (src/subst/simple_filename_exp.c:184) ==75845== by 0x10CD97: exec_external_cmd (src/executing/simple_cmd/simple_cmd_execute.c:67) ==75845== by 0x10CC58: simple_cmd_execute (src/executing/simple_cmd/simple_cmd_execute.c:94) ==75845== by 0x10B12C: cmd_execute (src/executing/cmd/cmd_execute.c:23) ==75845== by 0x10A4FD: execute_command (src/minishell.c:37) ==75845== by 0x10A3B2: main (src/minishell.c:90) ==75845== ==75845== LEAK SUMMARY: ==75845== definitely lost: 8 bytes in 1 blocks ==75845== indirectly lost: 0 bytes in 0 blocks ==75845== possibly lost: 0 bytes in 0 blocks ==75845== still reachable: 0 bytes in 0 blocks ==75845== suppressed: 4,193 bytes in 13 blocks ==75845== ==75845== For lists of detected and suppressed errors, rerun with: -s ==75845== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)inishell: cat: Permission denied
This commit is contained in:
parent
a86616f910
commit
9d37d07589
4 changed files with 189 additions and 147 deletions
|
|
@ -6,163 +6,18 @@
|
|||
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2025/03/02 13:40:10 by jguelen #+# #+# */
|
||||
/* Updated: 2025/04/01 18:38:35 by khais ### ########.fr */
|
||||
/* Updated: 2025/04/24 17:58:39 by khais ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "../minishell.h"
|
||||
#include "libft.h"
|
||||
#include "../ft_errno.h"
|
||||
#include "../env/env_manip.h"
|
||||
#include "path_split.h"
|
||||
#include "simple_filename_exp_utils_utils.h"
|
||||
#include "subst.h"
|
||||
#include <stdbool.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
/*
|
||||
** The return value of this function is always safe to destroy with
|
||||
** path_split_destroy.
|
||||
** Returns a NULL-terminated array containing each possible path contained
|
||||
** in the PATH environnement variable.
|
||||
** Returns NULL in case of an allocation error.
|
||||
*/
|
||||
static char **get_paths_array(t_env *env)
|
||||
{
|
||||
char **path_array;
|
||||
t_env *path_node;
|
||||
|
||||
path_node = env_find_node_bykey(env, "PATH");
|
||||
if (path_node == NULL || !path_node->value || !path_node->value[0])
|
||||
{
|
||||
path_array = ft_calloc(1, sizeof(char *));
|
||||
if (!path_array)
|
||||
return (NULL);
|
||||
return (path_array);
|
||||
}
|
||||
path_array = path_split(path_node->value, ':');
|
||||
if (!path_array)
|
||||
return (NULL);
|
||||
return (path_array);
|
||||
}
|
||||
|
||||
/*
|
||||
** Utility function that attempts to find a regular executable file for which
|
||||
** the execution rights are granted. It disregards non-regular files and so in
|
||||
** particular directories (note that using stat allows us to follow symlinks to
|
||||
** their target if encountered). If a file is found here for which the execution
|
||||
** permission is not granted and no elligible file was found before its path is
|
||||
** stored inside oldpath so that in case no file is found to be executable it
|
||||
** is the value stored in oldpath that will be used resulting in a Permission
|
||||
** denied error. oldpath is to store the first occurrence of a regular file
|
||||
** corresponding to the filepath but not executable.
|
||||
**
|
||||
** We disregard all stat(2) failures, since bash does the same and treats them
|
||||
** all to mean that the file does not exist.
|
||||
*/
|
||||
static char *select_path(char *filepath, char **oldpath, char **path,
|
||||
struct stat *fstat)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = stat(filepath, fstat);
|
||||
if (ret == -1)
|
||||
return (NULL);
|
||||
if (access(filepath, F_OK) == 0 && ret == 0 && S_ISREG(fstat->st_mode))
|
||||
{
|
||||
if (access(filepath, X_OK) != 0)
|
||||
{
|
||||
if (!(*oldpath))
|
||||
*oldpath = filepath;
|
||||
}
|
||||
else
|
||||
return (free(*oldpath), path_split_destroy(path), filepath);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function exists to implement the bash behaviour specific to where
|
||||
** the PATH variable has been set with a non-empty value.
|
||||
** It checks the working directory if there is a void inside PATH and tries to
|
||||
** find the executable command inside the path entry itself otherwise
|
||||
** disregarding directories and selecting the first entry that both exists and
|
||||
** is executable. If no such entry exists but one or more regular file(s)
|
||||
** exist(s) with the correct name in one of the entries (it therefore lacks the
|
||||
** execution permission) this function will return the first found.
|
||||
** Returns NULL if an error occurred or nothing corresponds to filename
|
||||
** in PATH. ft_errno is set in the course of this function to help distinguish
|
||||
** the nature of the case.
|
||||
*/
|
||||
static char *deal_with_filled_path(char *filename, char **path,
|
||||
struct stat *fstat)
|
||||
{
|
||||
size_t i;
|
||||
char *oldpath;
|
||||
char *filepath;
|
||||
char *tmp;
|
||||
|
||||
i = 0;
|
||||
oldpath = NULL;
|
||||
while (path[i])
|
||||
{
|
||||
if (!path[i][0])
|
||||
filepath = alloc_path(".", filename);
|
||||
else
|
||||
filepath = alloc_path(path[i], filename);
|
||||
if (!filepath)
|
||||
return (ft_errno(FT_ENOMEM), NULL);
|
||||
tmp = select_path(filepath, &oldpath, path, fstat);
|
||||
if (tmp)
|
||||
return (filepath);
|
||||
free(filepath);
|
||||
i++;
|
||||
}
|
||||
path_split_destroy(path);
|
||||
return (oldpath);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function searches the environment to resolve a full path for an
|
||||
** executable file corresponding to filename. It deals with two types of bash
|
||||
** behaviours: a PATH unset or corresponding to an empty value and a PATH set
|
||||
** and with a non-empty value.
|
||||
** In the first case it searches the current working directory for the file
|
||||
** coorresponding to filename. If no regular file is found it returns NULL and
|
||||
** distinguishes the case where a directory was found corresponding to filename
|
||||
** by setting ft_errno to FT_ISDIR.
|
||||
** The second case is dealt with using deal_with_filled_path (see the function
|
||||
** itself for details).
|
||||
** Returns NULL on error or if nothing is found.
|
||||
*/
|
||||
static char *filepath_from_env(char *filename, t_minishell *app)
|
||||
{
|
||||
char *filepath;
|
||||
char **path;
|
||||
struct stat fstat;
|
||||
int ret;
|
||||
|
||||
path = get_paths_array(app->env);
|
||||
if (path == NULL)
|
||||
return (ft_errno(FT_ENOMEM), NULL);
|
||||
if (!path[0])
|
||||
{
|
||||
filepath = alloc_path(".", filename);
|
||||
if (!filepath)
|
||||
return (ft_errno(FT_ENOMEM), NULL);
|
||||
ret = stat(filepath, &fstat);
|
||||
if (ret == -1)
|
||||
return (ft_errno(FT_STATFAIL), free(filepath), NULL);
|
||||
if (access(filepath, F_OK) == 0)
|
||||
{
|
||||
if (S_ISDIR(fstat.st_mode))
|
||||
return (free(filepath), ft_errno(FT_ISDIR), NULL);
|
||||
else if (S_ISREG(fstat.st_mode))
|
||||
return (filepath);
|
||||
}
|
||||
}
|
||||
return (deal_with_filled_path(filename, path, &fstat));
|
||||
}
|
||||
|
||||
/*
|
||||
** Returns a malloc-allocated string representing the full path to
|
||||
** the command or executable corresponding to name or NULL in case of
|
||||
|
|
|
|||
166
src/subst/simple_filename_exp_utils_utils.c
Normal file
166
src/subst/simple_filename_exp_utils_utils.c
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* simple_filename_exp_utils_utils.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2025/04/24 17:56:47 by khais #+# #+# */
|
||||
/* Updated: 2025/04/24 18:00:33 by khais ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "../ft_errno.h"
|
||||
#include "../env/env_manip.h"
|
||||
#include "simple_filename_exp_utils_utils.h"
|
||||
#include "subst.h"
|
||||
#include "path_split.h"
|
||||
|
||||
/*
|
||||
** The return value of this function is always safe to destroy with
|
||||
** path_split_destroy.
|
||||
** Returns a NULL-terminated array containing each possible path contained
|
||||
** in the PATH environnement variable.
|
||||
** Returns NULL in case of an allocation error.
|
||||
*/
|
||||
static char **get_paths_array(t_env *env)
|
||||
{
|
||||
char **path_array;
|
||||
t_env *path_node;
|
||||
|
||||
path_node = env_find_node_bykey(env, "PATH");
|
||||
if (path_node == NULL || !path_node->value || !path_node->value[0])
|
||||
{
|
||||
path_array = ft_calloc(1, sizeof(char *));
|
||||
if (!path_array)
|
||||
return (NULL);
|
||||
return (path_array);
|
||||
}
|
||||
path_array = path_split(path_node->value, ':');
|
||||
if (!path_array)
|
||||
return (NULL);
|
||||
return (path_array);
|
||||
}
|
||||
|
||||
/*
|
||||
** Utility function that attempts to find a regular executable file for which
|
||||
** the execution rights are granted. It disregards non-regular files and so in
|
||||
** particular directories (note that using stat allows us to follow symlinks to
|
||||
** their target if encountered). If a file is found here for which the execution
|
||||
** permission is not granted and no elligible file was found before its path is
|
||||
** stored inside oldpath so that in case no file is found to be executable it
|
||||
** is the value stored in oldpath that will be used resulting in a Permission
|
||||
** denied error. oldpath is to store the first occurrence of a regular file
|
||||
** corresponding to the filepath but not executable.
|
||||
**
|
||||
** We disregard all stat(2) failures, since bash does the same and treats them
|
||||
** all to mean that the file does not exist.
|
||||
*/
|
||||
static char *select_path(char *filepath, char **oldpath, char **path,
|
||||
struct stat *fstat)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = stat(filepath, fstat);
|
||||
if (ret == -1)
|
||||
return (NULL);
|
||||
if (access(filepath, F_OK) == 0 && ret == 0 && S_ISREG(fstat->st_mode))
|
||||
{
|
||||
if (access(filepath, X_OK) != 0)
|
||||
{
|
||||
if (!(*oldpath))
|
||||
*oldpath = filepath;
|
||||
}
|
||||
else
|
||||
return (free(*oldpath), path_split_destroy(path), filepath);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function exists to implement the bash behaviour specific to where
|
||||
** the PATH variable has been set with a non-empty value.
|
||||
** It checks the working directory if there is a void inside PATH and tries to
|
||||
** find the executable command inside the path entry itself otherwise
|
||||
** disregarding directories and selecting the first entry that both exists and
|
||||
** is executable. If no such entry exists but one or more regular file(s)
|
||||
** exist(s) with the correct name in one of the entries (it therefore lacks the
|
||||
** execution permission) this function will return the first found.
|
||||
** Returns NULL if an error occurred or nothing corresponds to filename
|
||||
** in PATH. ft_errno is set in the course of this function to help distinguish
|
||||
** the nature of the case.
|
||||
*/
|
||||
static char *deal_with_filled_path(char *filename, char **path,
|
||||
struct stat *fstat)
|
||||
{
|
||||
size_t i;
|
||||
char *oldpath;
|
||||
char *filepath;
|
||||
char *tmp;
|
||||
|
||||
i = 0;
|
||||
oldpath = NULL;
|
||||
while (path[i])
|
||||
{
|
||||
if (!path[i][0])
|
||||
filepath = alloc_path(".", filename);
|
||||
else
|
||||
filepath = alloc_path(path[i], filename);
|
||||
if (!filepath)
|
||||
return (ft_errno(FT_ENOMEM), NULL);
|
||||
tmp = select_path(filepath, &oldpath, path, fstat);
|
||||
if (tmp)
|
||||
return (filepath);
|
||||
free(filepath);
|
||||
i++;
|
||||
}
|
||||
path_split_destroy(path);
|
||||
return (oldpath);
|
||||
}
|
||||
|
||||
static char *filter_dir(struct stat fstat, char *filepath)
|
||||
{
|
||||
if (S_ISDIR(fstat.st_mode))
|
||||
return (free(filepath), ft_errno(FT_ISDIR), NULL);
|
||||
else if (S_ISREG(fstat.st_mode))
|
||||
return (filepath);
|
||||
return (free(filepath), NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function searches the environment to resolve a full path for an
|
||||
** executable file corresponding to filename. It deals with two types of bash
|
||||
** behaviours: a PATH unset or corresponding to an empty value and a PATH set
|
||||
** and with a non-empty value.
|
||||
** In the first case it searches the current working directory for the file
|
||||
** coorresponding to filename. If no regular file is found it returns NULL and
|
||||
** distinguishes the case where a directory was found corresponding to filename
|
||||
** by setting ft_errno to FT_ISDIR.
|
||||
** The second case is dealt with using deal_with_filled_path (see the function
|
||||
** itself for details).
|
||||
** Returns NULL on error or if nothing is found.
|
||||
*/
|
||||
char *filepath_from_env(char *filename, t_minishell *app)
|
||||
{
|
||||
char *filepath;
|
||||
char **path;
|
||||
struct stat fstat;
|
||||
int ret;
|
||||
|
||||
path = get_paths_array(app->env);
|
||||
if (path == NULL)
|
||||
return (ft_errno(FT_ENOMEM), NULL);
|
||||
if (!path[0])
|
||||
{
|
||||
path_split_destroy(path);
|
||||
filepath = alloc_path(".", filename);
|
||||
if (!filepath)
|
||||
return (ft_errno(FT_ENOMEM), NULL);
|
||||
ret = stat(filepath, &fstat);
|
||||
if (ret == -1)
|
||||
return (ft_errno(FT_STATFAIL), free(filepath), NULL);
|
||||
if (access(filepath, F_OK) == 0)
|
||||
return (filter_dir(fstat, filepath));
|
||||
}
|
||||
return (deal_with_filled_path(filename, path, &fstat));
|
||||
}
|
||||
20
src/subst/simple_filename_exp_utils_utils.h
Normal file
20
src/subst/simple_filename_exp_utils_utils.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* simple_filename_exp_utils_utils.h :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2025/04/24 17:57:22 by khais #+# #+# */
|
||||
/* Updated: 2025/04/24 18:00:56 by khais ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#ifndef SIMPLE_FILENAME_EXP_UTILS_UTILS_H
|
||||
# define SIMPLE_FILENAME_EXP_UTILS_UTILS_H
|
||||
|
||||
# include "../minishell.h"
|
||||
|
||||
char *filepath_from_env(char *filename, t_minishell *app);
|
||||
|
||||
#endif // SIMPLE_FILENAME_EXP_UTILS_UTILS_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue