diff options
author | Dan McDonald <danmcd@mnx.io> | 2022-10-04 10:23:07 -0400 |
---|---|---|
committer | Dan McDonald <danmcd@mnx.io> | 2022-10-04 10:23:07 -0400 |
commit | 06ed4b8d465c6c863197ec44ea3bf24587de157c (patch) | |
tree | 710b9d1784f40ac8a85c91072063b3ce6186fb1f /usr/src | |
parent | 80d7725bf1e4fb33051ad152f649814a6aea8d62 (diff) | |
parent | 37e2cd25d56b334a2403f2540a0b0a1e6a40bcd1 (diff) | |
download | illumos-joyent-06ed4b8d465c6c863197ec44ea3bf24587de157c.tar.gz |
[illumos-gate merge]
commit 37e2cd25d56b334a2403f2540a0b0a1e6a40bcd1
15031 event ports impacted by FDINFO accesses
commit 6a23a4b8ef29200f409ef8273bc56cdc0ec24e0a
15029 AD join observability
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c | 34 | ||||
-rw-r--r-- | usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c | 143 | ||||
-rw-r--r-- | usr/src/pkg/manifests/system-test-ostest.p5m | 2 | ||||
-rw-r--r-- | usr/src/test/os-tests/runfiles/default.run | 3 | ||||
-rw-r--r-- | usr/src/test/os-tests/tests/Makefile | 1 | ||||
-rw-r--r-- | usr/src/test/os-tests/tests/regression/Makefile | 53 | ||||
-rw-r--r-- | usr/src/test/os-tests/tests/regression/illumos-15031.c | 85 | ||||
-rw-r--r-- | usr/src/uts/common/fs/proc/prdata.h | 1 | ||||
-rw-r--r-- | usr/src/uts/common/fs/proc/prsubr.c | 49 | ||||
-rw-r--r-- | usr/src/uts/common/fs/proc/prvnops.c | 10 |
10 files changed, 342 insertions, 39 deletions
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c index 942650545f..9191cfbce6 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + * Copyright 2021 RackTop Systems, Inc. */ /* @@ -51,11 +52,11 @@ static DWORD mlsvc_join_rpc(smb_domainex_t *dxi, - char *admin_user, char *admin_pw, - char *machine_name, char *machine_pw); + char *admin_user, char *admin_pw, + char *machine_name, char *machine_pw); static DWORD mlsvc_join_noauth(smb_domainex_t *dxi, - char *machine_name, char *machine_pw); + char *machine_name, char *machine_pw); /* * This is called by smbd_dc_update just after we've learned about a @@ -96,6 +97,7 @@ mlsvc_join(smb_joininfo_t *info, smb_joinres_t *res) char machine_name[SMB_SAMACCT_MAXLEN]; char machine_pw[NETR_MACHINE_ACCT_PASSWD_MAX]; unsigned char passwd_hash[SMBAUTH_HASH_SZ]; + char addrbuf[INET6_ADDRSTRLEN]; smb_domainex_t dxi; smb_domain_t *di = &dxi.d_primary; DWORD status; @@ -126,8 +128,11 @@ mlsvc_join(smb_joininfo_t *info, smb_joinres_t *res) if (info->domain_username[0] != '\0') { (void) smb_auth_ntlm_hash(info->domain_passwd, passwd_hash); smb_ipc_set(info->domain_username, passwd_hash); + syslog(LOG_INFO, "smbd: joining with user %s", + info->domain_username); } else { smb_ipc_set(MLSVC_ANON_USER, zero_hash); + syslog(LOG_INFO, "smbd: joining with anonymous"); } /* @@ -139,6 +144,7 @@ mlsvc_join(smb_joininfo_t *info, smb_joinres_t *res) syslog(LOG_NOTICE, "Failed to set idmap domain name"); if (smb_config_refresh_idmap() != 0) syslog(LOG_NOTICE, "Failed to refresh idmap service"); + syslog(LOG_INFO, "smbd: set idmap domain %s", info->domain_name); /* Clear DNS local (ADS) lookup cache. */ smb_ads_refresh(B_FALSE); @@ -161,7 +167,11 @@ mlsvc_join(smb_joininfo_t *info, smb_joinres_t *res) * so that admin will know which AD server we were talking to. */ (void) strlcpy(res->dc_name, dxi.d_dci.dc_name, MAXHOSTNAMELEN); - syslog(LOG_INFO, "smbd: found AD server %s", dxi.d_dci.dc_name); + if (smb_inet_ntop(&dxi.d_dci.dc_addr, + addrbuf, sizeof (addrbuf)) == NULL) + strcpy(addrbuf, "?"); + syslog(LOG_INFO, "smbd: found AD server %s (%s)", + dxi.d_dci.dc_name, addrbuf); /* * Domain discovery needs to authenticate with the AD server. @@ -190,6 +200,13 @@ mlsvc_join(smb_joininfo_t *info, smb_joinres_t *res) */ /* + * Log info about the domain from ddiscover + */ + syslog(LOG_INFO, "smbd_join: domain FQN=%s", di->di_fqname); + syslog(LOG_INFO, "smbd_join: domain NBN=%s", di->di_nbname); + syslog(LOG_INFO, "smbd_join: domain SID=%s", di->di_sid); + + /* * Create or update our machine account on the DC. * A non-null user means we do "secure join". */ @@ -199,6 +216,7 @@ mlsvc_join(smb_joininfo_t *info, smb_joinres_t *res) */ status = NT_STATUS_UNSUCCESSFUL; if (ads_enabled) { + syslog(LOG_INFO, "use_ads=true (LDAP join)"); res->join_err = smb_ads_join(di->di_fqname, info->domain_username, info->domain_passwd, machine_pw); @@ -206,7 +224,7 @@ mlsvc_join(smb_joininfo_t *info, smb_joinres_t *res) status = NT_STATUS_SUCCESS; } } else { - syslog(LOG_DEBUG, "use_ads=false (do RPC join)"); + syslog(LOG_INFO, "use_ads=false (RPC join)"); /* * If ADS was disabled, join using RPC. @@ -294,8 +312,8 @@ out: static DWORD mlsvc_join_rpc(smb_domainex_t *dxi, - char *admin_user, char *admin_pw, - char *machine_name, char *machine_pw) + char *admin_user, char *admin_pw, + char *machine_name, char *machine_pw) { mlsvc_handle_t samr_handle; mlsvc_handle_t domain_handle; @@ -388,7 +406,7 @@ out_samr_handle: */ static DWORD mlsvc_join_noauth(smb_domainex_t *dxi, - char *machine_name, char *machine_pw) + char *machine_name, char *machine_pw) { char old_pw[SMB_SAMACCT_MAXLEN]; DWORD status; diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c index 44ae747bbf..ca2897aea1 100644 --- a/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2019 Nexenta by DDN, Inc. All rights reserved. + * Copyright 2021 RackTop Systems, Inc. */ #include <sys/param.h> @@ -422,7 +423,7 @@ again: NULL, /* ComputerName */ domain, NULL, /* DomainGuid */ - NULL, /* SiteName */ + NULL, /* SiteName */ flags, &dci); switch (status) { @@ -653,8 +654,10 @@ smb_ads_open_main(smb_ads_handle_t **hp, char *domain, char *user, if (user != NULL) { err = smb_kinit(domain, user, password); - if (err != 0) + if (err != 0) { + syslog(LOG_ERR, "smbns: kinit failed"); return (err); + } user = NULL; password = NULL; } @@ -729,6 +732,14 @@ smb_ads_open_main(smb_ads_handle_t **hp, char *domain, char *user, } (void) mutex_unlock(&smb_ads_cfg.c_mtx); + syslog(LOG_DEBUG, "smbns: smb_ads_open_main"); + syslog(LOG_DEBUG, "smbns: domain: %s", ah->domain); + syslog(LOG_DEBUG, "smbns: domain_dn: %s", ah->domain_dn); + syslog(LOG_DEBUG, "smbns: ip_addr: %s", ah->ip_addr); + syslog(LOG_DEBUG, "smbns: hostname: %s", ah->hostname); + syslog(LOG_DEBUG, "smbns: site: %s", + (ah->site != NULL) ? ah->site : ""); + rc = ldap_sasl_interactive_bind_s(ah->ld, "", "GSSAPI", NULL, NULL, LDAP_SASL_INTERACTIVE, &smb_ads_saslcallback, NULL); if (rc != LDAP_SUCCESS) { @@ -738,6 +749,7 @@ smb_ads_open_main(smb_ads_handle_t **hp, char *domain, char *user, free(ads_host); return (SMB_ADS_LDAP_SASL_BIND); } + syslog(LOG_DEBUG, "smbns: ldap_sasl_..._bind_s success"); free(ads_host); *hp = ah; @@ -1238,13 +1250,15 @@ smb_ads_get_dc_level(smb_ads_handle_t *ah) LDAPMessage *res, *entry; char *attr[2]; char **vals; - int rc = -1; + int rc; res = NULL; attr[0] = SMB_ADS_ATTR_DCLEVEL; attr[1] = NULL; - if (ldap_search_s(ah->ld, "", LDAP_SCOPE_BASE, NULL, attr, - 0, &res) != LDAP_SUCCESS) { + rc = ldap_search_s(ah->ld, "", LDAP_SCOPE_BASE, NULL, attr, 0, &res); + if (rc != LDAP_SUCCESS) { + syslog(LOG_ERR, "smb_ads_get_dc_level: " + "LDAP search, error %s", ldap_err2string(rc)); (void) ldap_msgfree(res); return (-1); } @@ -1255,6 +1269,7 @@ smb_ads_get_dc_level(smb_ads_handle_t *ah) return (-1); } + rc = -1; entry = ldap_first_entry(ah->ld, res); if (entry) { if ((vals = ldap_get_values(ah->ld, entry, @@ -1263,13 +1278,17 @@ smb_ads_get_dc_level(smb_ads_handle_t *ah) * Observed the values aren't populated * by the Windows 2000 server. */ + syslog(LOG_DEBUG, "smb_ads_get_dc_level: " + "LDAP values missing, assume W2K"); (void) ldap_msgfree(res); return (SMB_ADS_DCLEVEL_W2K); } - if (vals[0] != NULL) + if (vals[0] != NULL) { rc = atoi(vals[0]); - + syslog(LOG_DEBUG, "smb_ads_get_dc_level: " + "LDAP value %d", rc); + } ldap_value_free(vals); } @@ -1310,6 +1329,9 @@ smb_ads_computer_op(smb_ads_handle_t *ah, int op, int dclevel, char *dn) int max; smb_krb5_pn_set_t spn, upn; + syslog(LOG_DEBUG, "smb_ads_computer_op, op=%s dn=%s", + (op == LDAP_MOD_ADD) ? "add" : "replace", dn); + if (smb_getsamaccount(sam_acct, sizeof (sam_acct)) != 0) return (-1); @@ -1437,25 +1459,42 @@ smb_ads_getattr(LDAP *ld, LDAPMessage *entry, smb_ads_avpair_t *avpair) assert(avpair); avpair->avp_val = NULL; + + syslog(LOG_DEBUG, "smbns: ads_getattr (%s)", avpair->avp_attr); vals = ldap_get_values(ld, entry, avpair->avp_attr); - if (!vals) + if (!vals) { + syslog(LOG_DEBUG, "smbns: ads_getattr err: no vals"); return (SMB_ADS_STAT_NOT_FOUND); - + } if (!vals[0]) { + syslog(LOG_DEBUG, "smbns: ads_getattr err: no vals[0]"); ldap_value_free(vals); return (SMB_ADS_STAT_NOT_FOUND); } avpair->avp_val = strdup(vals[0]); - if (!avpair->avp_val) + if (!avpair->avp_val) { + syslog(LOG_DEBUG, "smbns: ads_getattr err: no mem"); rc = SMB_ADS_STAT_ERR; + } else { + syslog(LOG_DEBUG, "smbns: ads_getattr (%s) OK, val=%s", + avpair->avp_attr, avpair->avp_val); + } ldap_value_free(vals); return (rc); } /* - * Process query's result. + * Process query's result, making sure we have what we need. + * + * There's some non-obvious logic here for checking the returned + * DNS name for the machine account, trying to avoid modifying + * someone else's machine account. When we search for a machine + * account we always ask for the DNS name. For a pre-created + * machine account, the DNS name will be not set, and that's OK. + * If we see a DNS name and it doesn't match our DNS name, we'll + * assume the account belongs to someone else and return "DUP". */ static smb_ads_qstat_t smb_ads_get_qstat(smb_ads_handle_t *ah, LDAPMessage *res, @@ -1469,37 +1508,50 @@ smb_ads_get_qstat(smb_ads_handle_t *ah, LDAPMessage *res, if (smb_ads_getfqhostname(ah, fqhost, MAXHOSTNAMELEN)) return (SMB_ADS_STAT_ERR); - if (ldap_count_entries(ah->ld, res) == 0) + if (ldap_count_entries(ah->ld, res) == 0) { + syslog(LOG_DEBUG, "smbns: find_computer, " + "ldap_count_entries zero"); return (SMB_ADS_STAT_NOT_FOUND); + } - if ((entry = ldap_first_entry(ah->ld, res)) == NULL) + if ((entry = ldap_first_entry(ah->ld, res)) == NULL) { + syslog(LOG_DEBUG, "smbns: find_computer, " + "ldap_first_entry error"); return (SMB_ADS_STAT_ERR); + } + syslog(LOG_DEBUG, "smbns: find_computer, check DNS name"); dnshost_avp.avp_attr = SMB_ADS_ATTR_DNSHOST; rc = smb_ads_getattr(ah->ld, entry, &dnshost_avp); + /* + * Check the status of finding the DNS name + */ switch (rc) { case SMB_ADS_STAT_FOUND: /* - * Returns SMB_ADS_STAT_DUP to avoid overwriting + * Found a DNS name. If it doesn't match ours, + * returns SMB_ADS_STAT_DUP to avoid overwriting * the computer account of another system whose * NetBIOS name collides with that of the current * system. */ - if (strcasecmp(dnshost_avp.avp_val, fqhost)) + if (strcasecmp(dnshost_avp.avp_val, fqhost)) { + syslog(LOG_DEBUG, "smbns: find_computer, " + "duplicate name (%s)", + dnshost_avp.avp_val); rc = SMB_ADS_STAT_DUP; - + } free(dnshost_avp.avp_val); break; case SMB_ADS_STAT_NOT_FOUND: /* - * Pre-created computer account doesn't have - * the dNSHostname attribute. It's been observed - * that the dNSHostname attribute is only set after - * a successful domain join. - * Returns SMB_ADS_STAT_FOUND as the account is - * pre-created for the current system. + * No dNSHostname attribute, so probably a + * pre-created computer account. Use it. + * + * Returns SMB_ADS_STAT_FOUND for the status + * of finding the machine account. */ rc = SMB_ADS_STAT_FOUND; break; @@ -1511,11 +1563,13 @@ smb_ads_get_qstat(smb_ads_handle_t *ah, LDAPMessage *res, if (rc != SMB_ADS_STAT_FOUND) return (rc); - if (avpair) + if (avpair) { + syslog(LOG_DEBUG, "smbns: find_computer, check %s", + avpair->avp_attr); rc = smb_ads_getattr(ah->ld, entry, avpair); + } return (rc); - } /* @@ -1524,7 +1578,9 @@ smb_ads_get_qstat(smb_ads_handle_t *ah, LDAPMessage *res, * If avpair is NULL, checks the status of the specified computer account. * Otherwise, looks up the value of the specified computer account's attribute. * If found, the value field of the avpair will be allocated and set. The - * caller should free the allocated buffer. + * caller should free the allocated buffer. Caller avpair requests are: + * smb_ads_find_computer() asks for SMB_ADS_ATTR_DN + * smb_ads_lookup_computer_attr_kvno() SMB_ADS_ATTR_KVNO * * Return: * SMB_ADS_STAT_FOUND - if both the computer and the specified attribute is @@ -1544,6 +1600,7 @@ smb_ads_lookup_computer_n_attr(smb_ads_handle_t *ah, smb_ads_avpair_t *avpair, LDAPMessage *res; char sam_acct[SMB_SAMACCT_MAXLEN], sam_acct2[SMB_SAMACCT_MAXLEN]; smb_ads_qstat_t rc; + int err; if (smb_getsamaccount(sam_acct, sizeof (sam_acct)) != 0) return (SMB_ADS_STAT_ERR); @@ -1567,15 +1624,37 @@ smb_ads_lookup_computer_n_attr(smb_ads_handle_t *ah, smb_ads_avpair_t *avpair, "(&(objectClass=computer)(%s=%s))", SMB_ADS_ATTR_SAMACCT, sam_acct2); - if (ldap_search_s(ah->ld, dn, scope, filter, attrs, 0, - &res) != LDAP_SUCCESS) { + syslog(LOG_DEBUG, "smbns: lookup_computer, " + "dn=%s, scope=%d", dn, scope); + syslog(LOG_DEBUG, "smbns: lookup_computer, " + "filter=%s", filter); + syslog(LOG_DEBUG, "smbns: lookup_computer, " + "attrs[0]=%s", attrs[0]); + syslog(LOG_DEBUG, "smbns: lookup_computer, " + "attrs[1]=%s", attrs[1] ? attrs[1] : ""); + + err = ldap_search_s(ah->ld, dn, scope, filter, attrs, 0, &res); + if (err != LDAP_SUCCESS) { + syslog(LOG_DEBUG, "smbns: lookup_computer, " + "LDAP search failed, dn=(%s), scope=%d, err=%s", + dn, scope, ldap_err2string(err)); (void) ldap_msgfree(res); return (SMB_ADS_STAT_NOT_FOUND); } + syslog(LOG_DEBUG, "smbns: find_computer, ldap_search OK"); rc = smb_ads_get_qstat(ah, res, avpair); + if (rc == SMB_ADS_STAT_FOUND) { + syslog(LOG_DEBUG, "smbns: find_computer, attr %s = %s", + avpair->avp_attr, avpair->avp_val); + } else { + syslog(LOG_DEBUG, "smbns: find_computer, " + "get query status, error %d", rc); + } + /* free the search results */ (void) ldap_msgfree(res); + return (rc); } @@ -1594,6 +1673,8 @@ smb_ads_find_computer(smb_ads_handle_t *ah, char *dn) smb_ads_avpair_t avpair; avpair.avp_attr = SMB_ADS_ATTR_DN; + avpair.avp_val = NULL; + smb_ads_get_default_comp_container_dn(ah, dn, SMB_ADS_DN_MAX); stat = smb_ads_lookup_computer_n_attr(ah, &avpair, LDAP_SCOPE_ONELEVEL, dn); @@ -1659,6 +1740,7 @@ smb_ads_lookup_computer_attr_kvno(smb_ads_handle_t *ah, char *dn) int kvno = 1; avpair.avp_attr = SMB_ADS_ATTR_KVNO; + avpair.avp_val = NULL; if (smb_ads_lookup_computer_n_attr(ah, &avpair, LDAP_SCOPE_BASE, dn) == SMB_ADS_STAT_FOUND) { kvno = atoi(avpair.avp_val); @@ -1713,6 +1795,8 @@ smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd) rc = smb_ads_open_main(&ah, domain, user, usr_passwd); if (rc != 0) { + const char *s = smb_ads_strerror(rc); + syslog(LOG_ERR, "smb_ads_join: open_main, error %s", s); smb_ccache_remove(SMB_CCACHE_PATH); return (rc); } @@ -1727,6 +1811,8 @@ smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd) switch (qstat) { case SMB_ADS_STAT_FOUND: new_acct = B_FALSE; + syslog(LOG_INFO, "smb_ads_join: machine account found." + " Updating: %s", dn); if (smb_ads_modify_computer(ah, dclevel, dn) != 0) { smb_ads_close(ah); smb_ccache_remove(SMB_CCACHE_PATH); @@ -1737,6 +1823,8 @@ smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd) case SMB_ADS_STAT_NOT_FOUND: new_acct = B_TRUE; smb_ads_get_default_comp_dn(ah, dn, SMB_ADS_DN_MAX); + syslog(LOG_INFO, "smb_ads_join: machine account not found." + " Creating: %s", dn); if (smb_ads_add_computer(ah, dclevel, dn) != 0) { smb_ads_close(ah); smb_ccache_remove(SMB_CCACHE_PATH); @@ -1745,6 +1833,7 @@ smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd) break; default: + syslog(LOG_INFO, "smb_ads_find_computer, rc=%d", qstat); if (qstat == SMB_ADS_STAT_DUP) rc = SMB_ADJOIN_ERR_DUP_TRUST_ACCT; else diff --git a/usr/src/pkg/manifests/system-test-ostest.p5m b/usr/src/pkg/manifests/system-test-ostest.p5m index 2315c52658..100f109732 100644 --- a/usr/src/pkg/manifests/system-test-ostest.p5m +++ b/usr/src/pkg/manifests/system-test-ostest.p5m @@ -103,6 +103,8 @@ file path=opt/os-tests/tests/poll/poll_test mode=0555 dir path=opt/os-tests/tests/portfs file path=opt/os-tests/tests/portfs/file_assoc.32 mode=0555 file path=opt/os-tests/tests/portfs/file_assoc.64 mode=0555 +dir path=opt/os-tests/tests/regression +file path=opt/os-tests/tests/regression/illumos-15031 mode=0555 dir path=opt/os-tests/tests/sdevfs file path=opt/os-tests/tests/sdevfs/sdevfs_eisdir mode=0555 dir path=opt/os-tests/tests/secflags diff --git a/usr/src/test/os-tests/runfiles/default.run b/usr/src/test/os-tests/runfiles/default.run index e5dcabb596..49156ea42b 100644 --- a/usr/src/test/os-tests/runfiles/default.run +++ b/usr/src/test/os-tests/runfiles/default.run @@ -146,3 +146,6 @@ tests = ['coretests'] [/opt/os-tests/tests/portfs] tests = ['file_assoc.32', 'file_assoc.64'] + +[/opt/os-tests/tests/regression] +tests = ['illumos-15031'] diff --git a/usr/src/test/os-tests/tests/Makefile b/usr/src/test/os-tests/tests/Makefile index 6dfe29087d..832b750d48 100644 --- a/usr/src/test/os-tests/tests/Makefile +++ b/usr/src/test/os-tests/tests/Makefile @@ -29,6 +29,7 @@ SUBDIRS = \ pf_key \ poll \ portfs \ + regression \ sdevfs \ secflags \ sigqueue \ diff --git a/usr/src/test/os-tests/tests/regression/Makefile b/usr/src/test/os-tests/tests/regression/Makefile new file mode 100644 index 0000000000..1536e83ca6 --- /dev/null +++ b/usr/src/test/os-tests/tests/regression/Makefile @@ -0,0 +1,53 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2022 Oxide Computer Company +# + +PROGS = \ + illumos-15031 + +ROOTOPTDIR = $(ROOT)/opt/os-tests/tests +ROOTOPTREGRESSION = $(ROOTOPTDIR)/regression +ROOTOPTPROGS = $(PROGS:%=$(ROOTOPTREGRESSION)/%) + +include $(SRC)/cmd/Makefile.cmd + +CSTD=$(GNU_C99) + +.KEEP_STATE: + +all: $(PROGS) + +install: $(ROOTOPTPROGS) + +clean: + +$(ROOTOPTPROGS): $(PROGS) $(ROOTOPTREGRESSION) + +$(ROOTOPTDIR): + $(INS.dir) + +$(ROOTOPTREGRESSION): $(ROOTOPTDIR) + $(INS.dir) + +$(ROOTOPTREGRESSION)/%: % + $(INS.file) + +%: %.c + $(LINK.c) -o $@ $< $(LDLIBS) + $(POST_PROCESS) + +clobber: + $(RM) $(PROGS) + +FRC: diff --git a/usr/src/test/os-tests/tests/regression/illumos-15031.c b/usr/src/test/os-tests/tests/regression/illumos-15031.c new file mode 100644 index 0000000000..8f01ec6bc4 --- /dev/null +++ b/usr/src/test/os-tests/tests/regression/illumos-15031.c @@ -0,0 +1,85 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2022 Oxide Computer Company + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <port.h> +#include <err.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <sys/poll.h> +#include <stdbool.h> + +static bool +has_event(int pfd) +{ + port_event_t ev = { 0 }; + timespec_t ts = { 0 }; + + /* Because of illumos 14912, more care needs to be taken here */ + int res = port_get(pfd, &ev, &ts); + if (res != 0 || ev.portev_source == 0) { + return (false); + } else { + return (true); + } +} + +int +main(int argc, char *argv[]) +{ + int res; + int pipes[2]; + + res = pipe2(pipes, 0); + assert(res == 0); + + int pfd = port_create(); + assert(pfd >= 0); + + res = port_associate(pfd, PORT_SOURCE_FD, (uintptr_t)pipes[1], POLLIN, + NULL); + assert(res == 0); + + if (has_event(pfd)) { + errx(EXIT_FAILURE, "FAIL - unexpected early event"); + } + + char port_path[MAXPATHLEN]; + (void) sprintf(port_path, "/proc/%d/fd/%d", getpid(), pfd); + + /* incur the procfs FDINFO access */ + struct stat sbuf; + res = lstat(port_path, &sbuf); + assert(res == 0); + + /* write a byte to wake up the pipe */ + (void) write(pipes[0], port_path, 1); + + /* + * Check to see that the FDINFO access did not detach our registration + * for the event port. + */ + if (!has_event(pfd)) { + errx(EXIT_FAILURE, "FAIL - no event found"); + } + + (void) printf("PASS\n"); + return (EXIT_SUCCESS); +} diff --git a/usr/src/uts/common/fs/proc/prdata.h b/usr/src/uts/common/fs/proc/prdata.h index 336b4b0ed9..6d8ac2e6ed 100644 --- a/usr/src/uts/common/fs/proc/prdata.h +++ b/usr/src/uts/common/fs/proc/prdata.h @@ -354,6 +354,7 @@ extern int pr_set(proc_t *, long); extern int pr_unset(proc_t *, long); extern void pr_sethold(prnode_t *, sigset_t *); extern file_t *pr_getf(proc_t *, uint_t, short *); +extern void pr_releasef(file_t *); extern void pr_setfault(proc_t *, fltset_t *); extern int prusrio(proc_t *, enum uio_rw, struct uio *, int); extern int prreadargv(proc_t *, char *, size_t, size_t *); diff --git a/usr/src/uts/common/fs/proc/prsubr.c b/usr/src/uts/common/fs/proc/prsubr.c index 34e9c730f5..be41826b54 100644 --- a/usr/src/uts/common/fs/proc/prsubr.c +++ b/usr/src/uts/common/fs/proc/prsubr.c @@ -24,6 +24,7 @@ * Copyright 2019 Joyent, Inc. * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. * Copyright 2022 MNX Cloud, Inc. + * Copyright 2022 Oxide Computer Company */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -1622,6 +1623,54 @@ retry: return (fp); } + +/* + * Just as pr_getf() is a little unusual in how it goes about making the file_t + * safe for procfs consumers to access it, so too is pr_releasef() for safely + * releasing that "hold". The "hold" is unlike normal file descriptor activity + * -- procfs is just an interloper here, wanting access to the vnode_t without + * risk of a racing close() disrupting the state. Just as pr_getf() avoids some + * of the typical file_t behavior (such as auditing) when establishing its hold, + * so too should pr_releasef(). It should not go through the motions of + * closef() (since it is not a true close()) unless racing activity causes it to + * be the last actor holding the refcount above zero. + * + * Under normal circumstances, we expect to find file_t`f_count > 1 after + * the successful pr_getf() call. We are, after all, accessing a resource + * already held by the process in question. We would also expect to rarely race + * with a close() of the underlying fd, meaning that file_t`f_count > 1 would + * still holds at pr_releasef() time. That would mean we only need to decrement + * f_count, leaving it to the process to later close the fd (thus triggering + * VOP_CLOSE(), etc). + * + * It is only when that process manages to close() the fd while we have it + * "held" in procfs that we must make a trip through the traditional closef() + * logic to ensure proper tear-down of the file_t. + */ +void +pr_releasef(file_t *fp) +{ + mutex_enter(&fp->f_tlock); + if (fp->f_count > 1) { + /* + * This is the most common case: The file is still held open by + * the process, and we simply need to release our hold by + * decrementing f_count + */ + fp->f_count--; + mutex_exit(&fp->f_tlock); + } else { + /* + * A rare occasion: The process snuck a close() of this file + * while we were doing our business in procfs. Given that + * f_count == 1, we are the only one with a reference to the + * file_t and need to take a trip through closef() to free it. + */ + mutex_exit(&fp->f_tlock); + (void) closef(fp); + } +} + void pr_object_name(char *name, vnode_t *vp, struct vattr *vattr) { diff --git a/usr/src/uts/common/fs/proc/prvnops.c b/usr/src/uts/common/fs/proc/prvnops.c index 5e965b38c3..39f8e6f01e 100644 --- a/usr/src/uts/common/fs/proc/prvnops.c +++ b/usr/src/uts/common/fs/proc/prvnops.c @@ -25,6 +25,7 @@ * Copyright (c) 2017 by Delphix. All rights reserved. * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. * Copyright 2022 MNX Cloud, Inc. + * Copyright 2022 Oxide Computer Company */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -963,7 +964,7 @@ pr_read_fdinfo(prnode_t *pnp, uio_t *uiop, cred_t *cr) error = prgetfdinfo(p, fp->f_vnode, fdinfo, cr, fp->f_cred, &data); - (void) closef(fp); + pr_releasef(fp); out: if (error == 0) @@ -3386,7 +3387,7 @@ prgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, prunlock(pnp); vap->va_size = prgetfdinfosize(p, fp->f_vnode, cr); vap->va_nblocks = (fsblkcnt64_t)btod(vap->va_size); - (void) closef(fp); + pr_releasef(fp); return (0); } case PR_LWPDIR: @@ -4497,8 +4498,9 @@ pr_lookup_fddir(vnode_t *dp, char *comp) } prunlock(dpnp); - if (fp != NULL) - (void) closef(fp); + if (fp != NULL) { + pr_releasef(fp); + } if (vp == NULL) { prfreenode(pnp); |