https://bugs.openldap.org/show_bug.cgi?id=10508
Issue ID: 10508 Summary: syncrepl: be_modrdn / message_to_op called without orm_no_opattrs=1, modifiersName clobbered by consumer rootdn Product: OpenLDAP Version: 2.6.13 Hardware: i386 OS: Linux Status: UNCONFIRMED Keywords: needs_review Severity: normal Priority: --- Component: slapd Assignee: bugs@openldap.org Reporter: apostnikov@gmail.com Target Milestone: ---
Created attachment 1150 --> https://bugs.openldap.org/attachment.cgi?id=1150&action=edit 0001-syncrepl-set-orm_no_opattrs-for-backend-ops.patch
When syncrepl applies provider changes locally it invokes the backend through be_add / be_modify / be_modrdn / be_delete, carrying the provider's operational attributes in the modlist. Without `op->orm_no_opattrs = 1` the backend (verified on back-mdb; servers/slapd/back-mdb/modrdn.c:76 and modify.c:619) calls slap_mods_opattrs(), which auto-injects a `replace modifiersName=<op->o_dn>` mod whenever modifiersName is not already present in the modlist. For syncrepl, `op->o_dn` is the consumer's local rootdn (set at syncrepl.c:2165 — `op->o_dn = op->o_bd->be_rootdn`), so the consumer ends up with `modifiersName=cn=<consumer-rootdn>` instead of the provider's value.
ITS#4820 originally papered over this in syncrepl_diff_entry() with a kludge that always forced modifiersName/modifyTimestamp into the diff (`if (*mods && (modifiersName||modifyTimestamp)) attr_cmp(NULL,new)`). ITS#10250 commit 87933f3e removed that kludge with the rationale "the mod is being passed to the backend with orm_no_opattrs these days". But two code paths in syncrepl.c never actually set orm_no_opattrs:
1. syncrepl_entry() refresh-mode rename-with-content path (around line 4788 on master): the parallel be_modify branch sets `orm_no_opattrs = 1/0` around its call; the be_modrdn branch does not.
2. syncrepl_message_to_op() (delta-sync log replay; whole function from line 3180 on master): none of the four backend dispatches (be_add, be_modify, be_modrdn, be_delete) set the flag.
Once 87933f3e removed the kludge, the auto-inject surfaced whenever provider and consumer happened to already agree on modifiersName so syncrepl_diff_entry produced no mod for it. Whether that happens for a given entry depends on attribute iteration order in the diff (attribute hash bucket order), which is why x86_64/aarch64 mostly mask the bug while x86/armv7/armhf/s390x (QEMU builders) trigger it deterministically and ppc64le sees it as flake in test063.
Worst case the consumer ends up with the provider's entryCSN (the modrdn bumped it) but the wrong modifiersName, and ITS#10358's assert-retry then sees identical entryCSN, takes no further action, and the corruption is permanent until something else updates the entry.
Reproducer ----------------------------------------------------------------
Native: 32-bit or big-endian build of HEAD; run
cd tests make BACKEND=mdb mdb-mod TESTS=test017-syncreplication-refresh
Cross-arch via Alpine's CI image (any host):
docker run --rm --platform linux/386 -v $PWD:/mnt \
registry.alpinelinux.org/alpine/infra/docker/alpine-gitlab-ci:latest-x86 \ sh -c 'cd /mnt && ./configure --enable-modules --enable-mdb=mod \ --enable-syncprov=mod && make && cd tests && make mdb-mod'
Expected on affected builders:
>>>>> Starting test017-syncreplication-refresh for mdb... ... test failed - provider and consumer databases differ >>>>> Failed test017-syncreplication-refresh for mdb
Capturing the diff (modify defines.sh's CMPOUT temporarily) shows:
348c348 < description: Example, Inc. ITS test domain --- > description: Example, Inc. modify+modrdn test domain 350c350 < modifiersName: cn=Manager,dc=example,dc=com --- > modifiersName: cn=consumer,dc=example,dc=com
(Both entries carry the SAME final entryCSN — that's the smoking gun: the consumer adopted the provider's CSN via the modrdn, but the follow-up modify hit `err=122 LDAP_ASSERTION_FAILED` because of the CSN bump, and ITS#10358's retry then short-circuited.)
Proposed fix ----------------------------------------------------------------
Six added lines in servers/slapd/syncrepl.c — see attached patch 0001-syncrepl-set-orm_no_opattrs-for-backend-ops.patch. Tested against OPENLDAP_REL_ENG_2_6_13: full make test suite passes on x86 (linux/386 QEMU) including all six tests that ITS#10250 patched plus test043-delta-syncrepl and test063-delta-multiprovider.
References / related ITS ----------------------------------------------------------------
ITS#4820 — the original modifiersName/modifyTimestamp kludge added in 2007 ITS#10250 — kludge removed (MR#817, commit 87933f3e), exposing this bug ITS#10358 — assert-control retry (MR#781) — masks the divergence as silent data loss ITS#8852 — sorted_attr_cmp() prerequisite for ITS#10250 (commit 8986f99d)