1
0
forked from mirrors/pacman

Compare commits

...

11 Commits

Author SHA1 Message Date
morganamilo
83838214b7 Remove unused variables / assignments 2024-02-09 11:24:20 +10:00
morganamilo
4baeb8e40b libalpm: print errors when unknown keys in database 2024-02-09 11:24:20 +10:00
morganamilo
18b65ec909 pacman: remove uses of atoi 2024-02-09 11:24:20 +10:00
morganamilo
45ce932fd0 pacman: check for end of string when stripping ascii escapes 2024-02-09 11:24:20 +10:00
morganamilo
da4b590bce pacman: check if pkgname is empty instead of null in progress
The progress callback always uses an empty string when there is no
pkgname. However pacman checks if the argument is null.

Check if the string is empty instead and better document what is passed
to the progress callback.
2024-02-09 11:24:20 +10:00
Allan McRae
0649a66ee5 Add ALPM_PKG_REASON_UNKNOWN type
Return ALPM_PKG_REASON_UNKNOWN when parsing of %REASON% in the local
database fails.

Signed-off-by: Allan McRae <allan@archlinux.org>
2024-02-09 11:14:38 +10:00
morganamilo
6e6d3f18e3 libalpm: don't use atio for pkgreason
atio's behaviour is undefined if the input is not valid. Also it does
all sorts of whitespace and prefix handling which we don't need for
pkgreason.

Instead of going into UB on invalid input we now return EXPLICIT as the
fallback and print an error. However we don't actually error out as the
DB parsing tries to be error tolerant.

Signed-off-by: Allan McRae <allan@archlinux.org>
2024-02-09 11:14:38 +10:00
Andrew Gregory
0a394144b2 validate package metadata after loading
alpm has certain requirements for package metadata necessary for proper
functioning, name and version in particular.  These requirements are
already enforced in makepkg, but nowhere in alpm.

Exceptions are treated as errors for non-local packages because they
cannot be installed without potentially resulting in undefined behavior.
Exceptions for local packages are treated as warnings because they are
already installed, so any damage has already been done, and the user
would otherwise have no way to uninstall them.

Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com>
2024-02-07 12:27:26 +00:00
Andrew Gregory
fde59b99e8 be_package: delay freeing archive resource
The error path uconditinally tries to free the archive, leading to a
double-free segmentation fault if the error path is triggered after
already freeing it.

Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com>
2024-02-07 12:27:26 +00:00
Andrew Gregory
edd57c8b96 perform cleanup on sync db parsing errors
Cleanup was only being performed when libarchive failed to actually read
the file.

Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com>
2024-02-07 12:27:26 +00:00
Andrew Gregory
5c75a55c7d allow freeing partial db package cache
The free function was checking DB_STATUS_PKGCACHE, which is only set
once the package cache has been fully built.

Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com>
2024-02-07 12:27:26 +00:00
16 changed files with 153 additions and 19 deletions

View File

@@ -1149,7 +1149,9 @@ typedef enum _alpm_progress_t {
* make take a while to complete.
* @param ctx user-provided context
* @param progress the kind of event that is progressing
* @param pkg for package operations, the name of the package being operated on
* @param pkg the name of the package being operated on. if the progress kind
* is a packae operation (add, upgrade, downgrade, reinstall, remove).
* otherwise this will be an empty string.
* @param percent the percent completion of the action
* @param howmany the total amount of items in the action
* @param current the current amount of items completed
@@ -2293,7 +2295,9 @@ typedef enum _alpm_pkgreason_t {
/** Explicitly requested by the user. */
ALPM_PKG_REASON_EXPLICIT = 0,
/** Installed as a dependency for another package. */
ALPM_PKG_REASON_DEPEND = 1
ALPM_PKG_REASON_DEPEND = 1,
/** Failed parsing of local database */
ALPM_PKG_REASON_UNKNOWN = 2
} alpm_pkgreason_t;
/** Location a package object was loaded from. */

View File

@@ -630,6 +630,10 @@ static int local_db_populate(alpm_db_t *db)
continue;
}
/* treat local metadata errors as warning-only,
* they are already installed and otherwise they can't be operated on */
_alpm_pkg_check_meta(pkg);
/* add to the collection */
_alpm_log(db->handle, ALPM_LOG_FUNCTION, "adding '%s' to package cache for db '%s'\n",
pkg->name, db->treename);
@@ -650,6 +654,17 @@ static int local_db_populate(alpm_db_t *db)
return 0;
}
static alpm_pkgreason_t _read_pkgreason(alpm_handle_t *handle, const char *pkgname, const char *line) {
if(strcmp(line, "0") == 0) {
return ALPM_PKG_REASON_EXPLICIT;
} else if(strcmp(line, "1") == 0) {
return ALPM_PKG_REASON_DEPEND;
} else {
_alpm_log(handle, ALPM_LOG_ERROR, _("unknown install reason for package %s: %s\n"), pkgname, line);
return ALPM_PKG_REASON_UNKNOWN;
}
}
/* Note: the return value must be freed by the caller */
char *_alpm_local_db_pkgpath(alpm_db_t *db, alpm_pkg_t *info,
const char *filename)
@@ -772,7 +787,7 @@ static int local_db_read(alpm_pkg_t *info, int inforeq)
READ_AND_STORE(info->packager);
} else if(strcmp(line, "%REASON%") == 0) {
READ_NEXT();
info->reason = (alpm_pkgreason_t)atoi(line);
info->reason = _read_pkgreason(db->handle, info->name, line);
} else if(strcmp(line, "%VALIDATION%") == 0) {
alpm_list_t *i, *v = NULL;
READ_AND_STORE_ALL(v);
@@ -822,6 +837,12 @@ static int local_db_read(alpm_pkg_t *info, int inforeq)
}
}
FREELIST(lines);
} else {
_alpm_log(db->handle, ALPM_LOG_ERROR, _("%s: unknown key '%s' in sync database\n"), info->name, line);
alpm_list_t *lines = NULL;
READ_AND_STORE_ALL(lines);
FREELIST(lines);
}
}
fclose(fp);

View File

@@ -251,8 +251,10 @@ static int parse_descfile(alpm_handle_t *handle, struct archive *a, alpm_pkg_t *
return -1;
}
} else {
const char *pkgname = newpkg->name ? newpkg->name : "error";
_alpm_log(handle, ALPM_LOG_ERROR, _("%s: unknown key '%s' in package description\n"), pkgname, key);
_alpm_log(handle, ALPM_LOG_DEBUG, "%s: unknown key '%s' in description file line %d\n",
newpkg->name ? newpkg->name : "error", key, linenum);
pkgname, key, linenum);
}
}
}
@@ -652,8 +654,6 @@ alpm_pkg_t *_alpm_pkg_load_internal(alpm_handle_t *handle,
goto pkg_invalid;
}
_alpm_archive_read_free(archive);
/* internal fields for package struct */
newpkg->origin = ALPM_PKG_FROM_FILE;
STRDUP(newpkg->origin_data.file, pkgfile, goto error);
@@ -675,6 +675,11 @@ alpm_pkg_t *_alpm_pkg_load_internal(alpm_handle_t *handle,
newpkg->infolevel |= INFRQ_FILES;
}
if(_alpm_pkg_check_meta(newpkg) != 0) {
goto pkg_invalid;
}
_alpm_archive_read_free(archive);
close(fd);
return newpkg;

View File

@@ -344,6 +344,11 @@ static alpm_pkg_t *load_pkg_for_entry(alpm_db_t *db, const char *entryname,
pkg->ops = get_sync_pkg_ops();
pkg->handle = db->handle;
if(_alpm_pkg_check_meta(pkg) != 0) {
_alpm_pkg_free(pkg);
RET_ERR(db->handle, ALPM_ERR_PKG_INVALID, NULL);
}
/* add to the collection */
_alpm_log(db->handle, ALPM_LOG_FUNCTION, "adding '%s' to package cache for db '%s'\n",
pkg->name, db->treename);
@@ -452,6 +457,14 @@ static int sync_db_populate(alpm_db_t *db)
}
}
}
/* the db file was successfully read, but contained errors */
if(ret == -1) {
db->status &= ~DB_STATUS_VALID;
db->status |= DB_STATUS_INVALID;
_alpm_db_free_pkgcache(db);
GOTO_ERR(db->handle, ALPM_ERR_DB_INVALID, cleanup);
}
/* reading the db file failed */
if(archive_ret != ARCHIVE_EOF) {
_alpm_log(db->handle, ALPM_LOG_ERROR, _("could not read db '%s' (%s)\n"),
db->treename, archive_error_string(archive));
@@ -678,6 +691,11 @@ static int sync_db_read(alpm_db_t *db, struct archive *archive,
}
}
FREELIST(lines);
} else {
_alpm_log(db->handle, ALPM_LOG_ERROR, _("%s: unknown key '%s' in local database\n"), pkg->name, line);
alpm_list_t *lines = NULL;
READ_AND_STORE_ALL(lines);
FREELIST(lines);
}
}
if(ret != ARCHIVE_EOF) {

View File

@@ -563,18 +563,17 @@ static void free_groupcache(alpm_db_t *db)
void _alpm_db_free_pkgcache(alpm_db_t *db)
{
if(db == NULL || !(db->status & DB_STATUS_PKGCACHE)) {
if(db == NULL || db->pkgcache == NULL) {
return;
}
_alpm_log(db->handle, ALPM_LOG_DEBUG,
"freeing package cache for repository '%s'\n", db->treename);
if(db->pkgcache) {
alpm_list_free_inner(db->pkgcache->list,
(alpm_list_fn_free)_alpm_pkg_free);
_alpm_pkghash_free(db->pkgcache);
}
alpm_list_free_inner(db->pkgcache->list,
(alpm_list_fn_free)_alpm_pkg_free);
_alpm_pkghash_free(db->pkgcache);
db->pkgcache = NULL;
db->status &= ~DB_STATUS_PKGCACHE;
free_groupcache(db);

View File

@@ -556,7 +556,7 @@ static void _alpm_select_depends(alpm_list_t **from, alpm_list_t **to,
for(i = *from; i; i = next) {
alpm_pkg_t *deppkg = i->data;
next = i->next;
if((explicit || alpm_pkg_get_reason(deppkg) != ALPM_PKG_REASON_EXPLICIT)
if((explicit || alpm_pkg_get_reason(deppkg) == ALPM_PKG_REASON_DEPEND)
&& _alpm_pkg_depends_on(pkg, deppkg)) {
*to = alpm_list_add(*to, deppkg);
*from = alpm_list_remove_item(*from, i);

View File

@@ -573,7 +573,6 @@ static int curl_check_finished_download(alpm_handle_t *handle, CURLM *curlm, CUR
case CURLE_ABORTED_BY_CALLBACK:
/* handle the interrupt accordingly */
if(dload_interrupted == ABORT_OVER_MAXFILESIZE) {
curlerr = CURLE_FILESIZE_EXCEEDED;
payload->unlink_on_fail = 1;
handle->pm_errno = ALPM_ERR_LIBCURL;
_alpm_log(handle, ALPM_LOG_ERROR,

View File

@@ -21,6 +21,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
@@ -844,3 +845,58 @@ int SYMEXPORT alpm_pkg_should_ignore(alpm_handle_t *handle, alpm_pkg_t *pkg)
return 0;
}
/* check that package metadata meets our requirements */
int _alpm_pkg_check_meta(alpm_pkg_t *pkg)
{
char *c;
int error_found = 0;
#define EPKGMETA(error) do { \
error_found = -1; \
_alpm_log(pkg->handle, ALPM_LOG_ERROR, error, pkg->name, pkg->version); \
} while(0)
/* sanity check */
if(pkg->handle == NULL) {
return -1;
}
/* immediate bail if package doesn't have name or version */
if(pkg->name == NULL || pkg->name[0] == '\0'
|| pkg->version == NULL || pkg->version[0] == '\0') {
_alpm_log(pkg->handle, ALPM_LOG_ERROR,
_("invalid package metadata (name or version missing)"));
return -1;
}
if(pkg->name[0] == '-' || pkg->name[0] == '.') {
EPKGMETA(_("invalid metadata for package %s-%s "
"(package name cannot start with '.' or '-')\n"));
}
if(_alpm_fnmatch(pkg->name, "[![:alnum:]+_.@-]") == 0) {
EPKGMETA(_("invalid metadata for package %s-%s "
"(package name contains invalid characters)\n"));
}
/* multiple '-' in pkgver can cause local db entries for different packages
* to overlap (e.g. foo-1=2-3 and foo=1-2-3 both give foo-1-2-3) */
if((c = strchr(pkg->version, '-')) && (strchr(c + 1, '-'))) {
EPKGMETA(_("invalid metadata for package %s-%s "
"(package version contains invalid characters)\n"));
}
if(strchr(pkg->version, '/')) {
EPKGMETA(_("invalid metadata for package %s-%s "
"(package version contains invalid characters)\n"));
}
/* local db entry is <pkgname>-<pkgver> */
if(strlen(pkg->name) + strlen(pkg->version) + 1 > NAME_MAX) {
EPKGMETA(_("invalid metadata for package %s-%s "
"(package name and version too long)\n"));
}
#undef EPKGMETA
return error_found;
}

View File

@@ -165,4 +165,6 @@ int _alpm_pkg_compare_versions(alpm_pkg_t *local_pkg, alpm_pkg_t *pkg);
alpm_pkg_xdata_t *_alpm_pkg_parse_xdata(const char *string);
void _alpm_pkg_xdata_free(alpm_pkg_xdata_t *pd);
int _alpm_pkg_check_meta(alpm_pkg_t *pkg);
#endif /* ALPM_PACKAGE_H */

View File

@@ -589,7 +589,7 @@ void cb_progress(void *ctx, alpm_progress_t event, const char *pkgname,
} else {
if(current != prevcurrent) {
/* update always */
} else if(!pkgname || percent == prevpercent ||
} else if(pkgname[0] == '\0' || percent == prevpercent ||
get_update_timediff(0) < UPDATE_SPEED_MS) {
/* only update the progress bar when we have a package name, the
* percentage has changed, and it has been long enough. */
@@ -653,7 +653,7 @@ void cb_progress(void *ctx, alpm_progress_t event, const char *pkgname,
* by the output, and then pad it accordingly so we fill the terminal.
*/
/* len = opr len + pkgname len (if available) + space + null */
len = strlen(opr) + ((pkgname) ? strlen(pkgname) : 0) + 2;
len = strlen(opr) + strlen(pkgname) + 2;
wcstr = calloc(len, sizeof(wchar_t));
/* print our strings to the alloc'ed memory */
#if defined(HAVE_SWPRINTF)

View File

@@ -381,7 +381,7 @@ static int parsearg_global(int opt)
break;
case OP_ASK:
config->noask = 1;
config->ask = (unsigned int)atoi(optarg);
config->ask = (unsigned int)strtol(optarg, NULL, 10);
break;
case OP_CACHEDIR:
config->cachedirs = alpm_list_add(config->cachedirs, strdup(optarg));
@@ -409,7 +409,7 @@ static int parsearg_global(int opt)
* here, error and warning are set in config_new, though perhaps a
* --quiet option will remove these later */
if(optarg) {
unsigned short debug = (unsigned short)atoi(optarg);
int debug = strtol(optarg, NULL, 10);
switch(debug) {
case 2:
config->logmask |= ALPM_LOG_FUNCTION;

View File

@@ -462,7 +462,7 @@ static size_t string_length(const char *s)
int iter = 0;
for(; *s; s++) {
if(*s == '\033') {
while(*s != 'm') {
while(*s != 'm' && *s != '\0') {
s++;
}
} else {

View File

@@ -97,6 +97,9 @@ pacman_tests = [
'tests/pacman003.py',
'tests/pacman004.py',
'tests/pacman005.py',
'tests/pkg-meta-invalid-name-file.py',
'tests/pkg-meta-invalid-name-local.py',
'tests/pkg-meta-invalid-name-sync.py',
'tests/provision001.py',
'tests/provision002.py',
'tests/provision003.py',

View File

@@ -0,0 +1,9 @@
self.description = "package name with invalid characters cannot be installed (file)"
p = pmpkg("-foo")
self.addpkg(p)
self.args = "-U -- %s" % p.filename()
self.addrule("!PACMAN_RETCODE=0")
self.addrule("!PKG_EXIST=-foo")

View File

@@ -0,0 +1,9 @@
self.description = "local package name with invalid characters can be removed"
sp = pmpkg("-foo")
self.addpkg2db("local", sp)
self.args = "-R -- %s" % sp.name
self.addrule("PACMAN_RETCODE=0")
self.addrule("!PKG_EXIST=-foo")

View File

@@ -0,0 +1,9 @@
self.description = "package name with invalid characters cannot be installed"
sp = pmpkg("-foo")
self.addpkg2db("sync", sp)
self.args = "-S -- %s" % sp.name
self.addrule("!PACMAN_RETCODE=0")
self.addrule("!PKG_EXIST=-foo")