The string tacked onto modular package names — the +4012+cd64a238 in perl-FCGI-0.78-12.module_el8.10.0+4012+cd64a238.x86_64 — is a build identifier stamped in by the distribution's build system: an incrementing build index followed by a hash of the module's identity. Neither component encodes how many security patches a package contains, so a vulnerability report demanding a "higher" number there is comparing the wrong thing. The authoritative check is the vendor's security advisory matched against the complete installed package, including architecture and module stream.
Anatomy of the suffix
Splitting the example name apart makes each field's role clear:
perl-FCGI— the package name.0.78— the upstream software version.12— the release counter the package maintainer increments..module_el8.10.0— a marker that this RPM was produced for a module stream on EL 8.10.+4012— a build index: a counter that advances each time the build system produces a new build of the module.+cd64a238— a short hash, specifically the first eight hexadecimal characters of a SHA-1 digest computed over the module's name, stream, version, and context.x86_64— the architecture.
Everything from .module_ onward is part of the RPM Release field, not the upstream version. AlmaLinux's build service constructs it in the form .module_<dist prefix>+<build index>+<hash>, which you can confirm in the build-system source if you want the exact recipe.
A higher build index is not a patch level
The index changes whenever a new build occurs — for a security fix, a bug fix, or even a routine rebuild — so a larger number means only "later build." Whether that particular build carries a given CVE fix is established by the advisory that shipped it, not by the counter.
More importantly for automated comparisons, the index is not consistent across architectures. AlmaLinux advisory ALSA-2025:8696, which addresses CVE-2025-40907 in perl-FCGI, shipped both of these updated packages in the same erratum:
ppc64le: perl-FCGI-0.78-12.module_el8.10.0+4012+cd64a238.ppc64le
x86_64: perl-FCGI-0.78-12.module_el8.10.0+4013+e6a7e623.x86_64
Both are the fixed builds. That means 4013 is not a universal minimum version, and 4012 does not inherently mean "still vulnerable." A compliance rule requiring ≥4013 on every host would flag correctly patched ppc64le machines as broken.
There is a subtlety in the case that motivates this article, though: the advisory pairs the 4012+cd64a238 string with ppc64le, while the installed package in question is x86_64. Those are different binaries. An x86_64 system still running the 4012 build may genuinely be missing the fix, because the fixed x86_64 build is the 4013 one. The suffix alone cannot settle the finding in either direction — the advisory plus your architecture can.
Modularity made identity comparisons harder
Traditional RPM identity is NEVRA: name, epoch, version, release, architecture. Modules layer a second identity on top — name, stream, version, context, architecture (NSVCA) — and the build system folds the module's version and context into the RPM's release string. This was always an implementation detail of the build tooling, thinly documented even when modularity was an actively developed feature, and it is not uniform across vendors: Red Hat's own builds for the same CVE kept the same stream version on every architecture, while AlmaLinux's rebuilds did not. The practical rule is simple: never compare a fragment of the string. Compare the whole release, the architecture, and the module stream.
Verifying a finding the right way
- Locate the vendor advisory for the CVE —
errata.almalinux.orgfor AlmaLinux, the Red Hat errata for RHEL — and note the exact fixed package string for your architecture. - Capture the full identity of what is installed with
rpm -q perl-FCGI, and check which module stream is in use withdnf module list. - Look for the CVE in the package's own metadata:
rpm -q --changelog perl-FCGI | grep -i cveMaintainers conventionally cite CVE and bug IDs in the changelog of security builds. In this case the history shows perl-FCGI has remained at upstream version 0.78 while the maintainer release advanced, with the security build recorded as
1:0.78-12referencing the CVE — direct evidence on the installed system. - Apply the update with module-aware tooling (
dnf update perl-FCGI, ordnf module updatefor the whole stream) and confirm the installed string now matches the advisory.
When the "fixed" build seems unavailable
If your repositories do not offer the exact fixed package the advisory names, work through the usual suspects before assuming anything:
- Query what is actually published with
dnf repoquery perl-FCGI --showduplicates. The build number your scanner expects may belong to a different architecture's package. - Confirm that the enabled module stream — and its context — is the one the advisory targets. Fixes land per stream, and an outdated or switched stream will not see them.
- Refresh metadata (
dnf clean metadata && dnf makecache); mirror synchronization lag can hide a freshly published build for some time.
Hardening your patch process
- Never key scanner exceptions or sign-off criteria to the build index; it is a build-system counter, not a security metric.
- Record the full NEVRA plus the module stream in your inventories so audits compare like with like.
- Use changelog CVE references as evidence that a fix is present.
- Where the risk justifies it, verify the fix functionally by exercising the vulnerable behavior; in most cases, however, confirming the vendor's fixed package is installed is sufficient and far cheaper.
Frequently Asked Questions
Does a bigger number after the plus sign mean more security patches?
No. It is a build index that increments with each new build of the module, regardless of why the build was made. Patch content is established by the advisory that shipped the build, not by the counter.
What is the hexadecimal string after the build number?
It is the first eight hex characters of a SHA-1 digest over the module's name, stream, version, and context. It identifies which module build context produced the RPM — it says nothing about patches.
Can the same fix ship with different build numbers on different architectures?
Yes. AlmaLinux's advisory for CVE-2025-40907 shipped +4012+cd64a238 for ppc64le and +4013+e6a7e623 for x86_64 in the same erratum, both fixed. Comparing the number alone will produce false positives.
How do I prove my system actually has the CVE fix?
Match the full installed package string, architecture, and module stream against the vendor advisory, and check rpm -q --changelog for the CVE identifier — maintainers cite it in security builds.
Why can't my package manager find the fixed version my scanner wants?
The scanner's expected string may be for another architecture, your enabled module stream may not be the one the advisory targets, or mirrors may not have synced the new build yet. Use dnf repoquery --showduplicates and refresh metadata to check.
Is the +4012 part of the upstream version 0.78?
No. The entire .module_el8.10.0+4012+cd64a238 tail belongs to the RPM Release field. The upstream version remains 0.78.