php-fpm listen.acl_users and listen.acl_groups not checking for duplicate ACL entries causing `Invalid argument (22)`
还没有人认领这个 Issue。
- 主要语言
- C
- 星标
- 40.4k
- 派生
- 8.1k
- 平均合并
- 2 天 13 小时
- 30 天内合并 PR
- 96
描述
Description
if listen.acl_users or listen.acl_groups directives in an FPM pool config define entries that are already present in the socket ACL via having the default ACL on the parent directory, the whole FPM fails to start with something akin
ERROR: [pool %s] failed to write the ACL of the socket '%s': Invalid argument (22)
ERROR: FPM initialization failed
quick search led me to int fpm_unix_set_socket_permissions(struct fpm_worker_pool_s *wp, const char *path) that does not check the resulting ACL for entries with duplicate uids or gids and thus aborting FPM initialization
a simple testcase would be
mkdir acl-test
setfacl -m d:g:www-data:rx acl-test
and trying to start FPM with a pool like
[acl-test]
listen = acl-test/php.socket
listen.acl_groups = www-data
it seems the whole if (wp->socket_acl) case should look like this:
if (wp->socket_acl) {
acl_t aclfile, aclconf;
acl_entry_t entryfile, entryconf;
int ifile, iconf;
acl_tag_t tagfile, tagconf;
uid_t *uidfile, *uidconf;
gid_t *gidfile, *gidconf;
/* Read the socket ACL */
aclconf = wp->socket_acl;
aclfile = acl_get_file (path, ACL_TYPE_ACCESS);
if (!aclfile) {
zlog(ZLOG_SYSERROR, "[pool %s] failed to read the ACL of the socket '%s'", wp->config->name, path);
return -1;
}
/* Copy the new ACL entry from config */
for (iconf=ACL_FIRST_ENTRY ; acl_get_entry(aclconf, iconf, &entryconf) ; iconf=ACL_NEXT_ENTRY) {
if (0 > acl_get_tag_type(entryconf, &tagconf)) {
zlog(ZLOG_SYSERROR, "[pool %s] failed to get tag of the ACL entry of the pool", wp->config->name);
acl_free(aclfile);
return -1;
}
if (tagconf == ACL_USER) {
uidconf = acl_get_qualifier(entryfile);
if (!uidconf) {
zlog(ZLOG_SYSERROR, "[pool %s] failed to get user qualifier of the ACL entry of the pool", wp->config->name);
acl_free(aclfile);
return -1;
}
} else {
gidconf = acl_get_qualifier(entryfile);
if (!gidconf) {
zlog(ZLOG_SYSERROR, "[pool %s] failed to get group qualifier of the ACL entry of the pool", wp->config->name);
acl_free(aclfile);
return -1;
}
}
for (ifile=ACL_FIRST_ENTRY ; acl_get_entry(aclfile, ifile, &entryfile) ; ifile=ACL_NEXT_ENTRY) {
if (0 > acl_get_tag_type(entryfile, &tagfile)) {
zlog(ZLOG_SYSERROR, "[pool %s] failed to get tag of the ACL entry of the socket '%s'", wp->config->name, path);
acl_free(tagconf == ACL_USER ? uidconf : gidconf);
acl_free(aclfile);
return -1;
}
if (tagfile != ACL_USER && tagfile != ACL_GROUP)
continue;
if (tagfile != tagconf)
continue;
if (tagfile == ACL_USER) {
uidfile = acl_get_qualifier(entryfile);
if (!uidfile) {
zlog(ZLOG_SYSERROR, "[pool %s] failed to get user qualifier of the ACL entry of the socket '%s'", wp->config->name, path);
acl_free(uidconf);
acl_free(aclfile);
return -1;
}
if (*uidfile != *uidconf) {
acl_free(uidfile);
continue;
}
} else {
gidfile = acl_get_qualifier(entryfile);
if (!gidfile) {
zlog(ZLOG_SYSERROR, "[pool %s] failed to get group qualifier of the ACL entry of the socket '%s'", wp->config->name, path);
acl_free(gidconf);
acl_free(aclfile);
return -1;
}
if (*gidfile != *gidconf) {
acl_free(gidfile);
continue;
}
}
acl_free(tagfile == ACL_USER ? uidfile : gidfile);
acl_delete_entry(aclfile, entryfile);
}
acl_free(tagconf == ACL_USER ? uidconf : gidconf);
if (0 > acl_create_entry (&aclfile, &entryfile) ||
0 > acl_copy_entry(entryfile, entryconf)) {
zlog(ZLOG_SYSERROR, "[pool %s] failed to add entry to the ACL of the socket '%s'", wp->config->name, path);
acl_free(aclfile);
return -1;
}
}
/* Write the socket ACL */
if (0 > acl_calc_mask (&aclfile) ||
0 > acl_valid (aclfile) ||
0 > acl_set_file (path, ACL_TYPE_ACCESS, aclfile)) {
zlog(ZLOG_SYSERROR, "[pool %s] failed to write the ACL of the socket '%s'", wp->config->name, path);
acl_free(aclfile);
return -1;
} else {
zlog(ZLOG_DEBUG, "[pool %s] ACL of the socket '%s' is set", wp->config->name, path);
}
acl_free(aclfile);
return 0;
}
the code above is untested, as I currently have no desire to delve deep into php build process
PHP Version
PHP 8.4.6 (fpm-fcgi) (built: Apr 11 2025 02:09:29) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.4.6, Copyright (c) Zend Technologies
with Zend OPcache v8.4.6, Copyright (c), by Zend Technologies
Operating System
Debian 12
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
调研方向
从 sapi/fpm/fpm/fpm_unix.c 中的 fpm_unix_set_socket_permissions() 开始,然后使用 setfacl 命令和所示的 FPM pool 配置重现失败。跟踪现有 socket ACL 条目如何与 listen.acl_groups 或 listen.acl_users 组合。完成的标准是,重复的用户或组条目不再导致 ACL 写入或 FPM 初始化失败。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- c, php
- 领域
- backend, operating-systems
- Issue 类型
- 缺陷
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 活跃度
- 停滞
- 描述清晰度
- 基本清楚
- 新手友好度
- 42/100