php / php/php-src

php-fpm listen.acl_users and listen.acl_groups not checking for duplicate ACL entries causing `Invalid argument (22)`

Đang mở
#18,357 1 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

Bug SAPI: fpm
Ngôn ngữ chính
C
Star
40.4k
Fork
8.2k
Merge trung bình
2 ngày 13 giờ
Pull request đã merge (30 ngày)
96

Mô tả

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

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Hướng nghiên cứu

Bắt đầu trong sapi/fpm/fpm/fpm_unix.c tại fpm_unix_set_socket_permissions(), sau đó tái hiện lỗi bằng lệnh setfacl và cấu hình pool FPM được nêu. Theo dõi cách các mục ACL hiện có của socket được kết hợp với listen.acl_groups hoặc listen.acl_users. Công việc được xem là hoàn tất khi các mục người dùng hoặc nhóm bị trùng không còn khiến việc ghi ACL hoặc khởi tạo FPM thất bại.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
c, php
Lĩnh vực
backend, operating-systems
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Đình trệ
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
42/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.