Bug 37373

Summary: dynamic memory allocation spoils memory (on e2k)
Product: Sisyphus Reporter: Ivan Zakharyaschev <imz>
Component: aptAssignee: Ivan Zakharyaschev <imz>
Status: NEW --- QA Contact: qa-sisyphus
Severity: major    
Priority: P3 CC: bircoph, boyarsh, darktemplar, glebfm, imz, ldv, placeholder, rider
Version: unstable   
Hardware: e2k   
OS: Linux   
Bug Depends on:    
Bug Blocks: 30482    

Description Ivan Zakharyaschev 2019-10-24 17:42:56 MSK
0.5.15lorg2-alt71 , appeared in: 0.5.15lorg2-alt70
- Ported dynamic memory allocation from Debian.
- Bumped soname due to ABI changes.

После этого возникли ошибки apt на e2k.

# Как воспроизвести проблему в hasher.

Создаём ~/hasher/ на основе текущего репозитория sisyphus_e2k:

    [imz@e801-1 ~]$ cat /home/imz/.hasher/sisyphus_e2k/apt.conf
    #include "/home/imz/.hasher/extra-apt.conf";
    
    Dir::Etc {
    	 sourcelist "/home/imz/.hasher/sisyphus_e2k/sources.list";
    	 sourceparts "/var/empty/";
    };
    [imz@e801-1 ~]$ cat /home/imz/.hasher/sisyphus_e2k/sources.list
    rpm file:/girar_repo sisyphus_e2k/release/latest/e2kv4 classic debuginfo
    rpm file:/girar_repo sisyphus_e2k/release/latest/noarch classic
    [imz@e801-1 ~]$ hsh --apt-config=/home/imz/.hasher/sisyphus_e2k/apt.conf ~/hasher/ --ini

Устанавливаем туда apt (update-kernel его вытянет) и ssh-клиент,
который понадобится нам для внутрихешерного sources.list:

    [imz@e801-1 ~]$ hsh-install ~/hasher/ update-kernel openssh-clients

Делаем ключ для доступа по ssh изнутри хешера и регистрируем его:

    [imz@e801-1 ~]$ hsh-shell ~/hasher/ --mount=/dev/pts,/proc --root
    [root@localhost .in]# ssh-keygen
    [root@localhost .in]# cp ~/.ssh/id_rsa.pub /
    [root@localhost .in]# exit
    [imz@e801-1 ~]$ cat ~/hasher/chroot/id_rsa.pub >>~/.ssh/authorized_keys
    [imz@e801-1 ~]$ share_network=1 hsh-shell ~/hasher-apt0-debug/ --mount=/dev/pts,/proc --root
    mesg: /dev/pts/0: Read-only file system
    [root@localhost .in]# ssh imz@localhost
    The authenticity of host 'localhost (127.0.0.1)' can't be established.
    ED25519 key fingerprint is SHA256:igk5kP1YVX/HjtAH/v5vWpaiRfg54zNmkPv39u0rQXE.
    Are you sure you want to continue connecting (yes/no)? yes
    Warning: Permanently added 'localhost' (ED25519) to the list of known hosts.
    Last login: Wed Oct  2 15:29:05 2019 from 10.13.0.22
    [imz@e801-1 ~]$ exit
    [root@localhost .in]# exit

Готовим sources.list для внутрихешерного apt-а:

    [imz@e801-1 ~]$ cat ~/apt-sources.ssh-in-hsh.list
    # -*- conf -*-
    rpm ssh://imz@localhost/girar_repo sisyphus_e2k/release/latest/e2kv4 classic debuginfo
    rpm ssh://imz@localhost/girar_repo sisyphus_e2k/release/latest/noarch classic
    
    [imz@e801-1 ~]$ cp -fv ~/apt-sources.ssh-in-hsh.list -T ~/hasher/chroot/.in/sources.list
    '/home/imz/apt-sources.ssh-in-hsh.list' -> '/home/imz/hasher/chroot/.in/sources.list'
    [imz@e801-1 ~]$ hsh-run ~/hasher/ --mount=/dev/pts,/proc --root -- cp -fv sources.list -t /etc/apt/
    'sources.list' -> '/etc/apt/sources.list'
    [imz@e801-1 ~]$ 

Тестируем `apt-get update` в ~/hasher/ с новым apt-ом:

    [imz@e801-1 ~]$ share_network=1 hsh-run ~/hasher/ --mount=/dev/pts,/proc --root -- apt-get update
    Get:1 ssh://imz@localhost sisyphus_e2k/release/latest/e2kv4 release [1544B]
    Get:2 ssh://imz@localhost sisyphus_e2k/release/latest/noarch release [1338B]
    Fetched 2882B in 0s (10.4kB/s)
    Hit ssh://imz@localhost sisyphus_e2k/release/latest/e2kv4/classic pkglist
    Hit ssh://imz@localhost sisyphus_e2k/release/latest/e2kv4/classic release
    Hit ssh://imz@localhost sisyphus_e2k/release/latest/e2kv4/debuginfo pkglist
    Hit ssh://imz@localhost sisyphus_e2k/release/latest/e2kv4/debuginfo release
    Hit ssh://imz@localhost sisyphus_e2k/release/latest/noarch/classic pkglist
    Hit ssh://imz@localhost sisyphus_e2k/release/latest/noarch/classic release
    Reading Package Lists...
    E: Ran out of allocation pools
    E: Ran out of allocation pools
    E: Error occured while processing docx2txt (NewVersion2)
    E: Problem with MergeList /var/lib/apt/lists/imz%40localhost_girar%5frepo_sisyphus%5fe2k_release_latest_noarch_base_pkglist.classic
    E: The package lists or status file could not be parsed or opened.
    [imz@e801-1 ~]$ 

Если кому-то понадобится, в такой ситуации есть workaround. (Хотя
сейчас для e2k apt уже собран alt71.1.1 с объединением всех предложенных ради
исправления изменений и больше не ошибаются. Изменения обсуждаются
ниже с тем, чтобы прийти к более чистому исправлению и пониманию причин.)

## Workaround

Есть рекомендовавшиеся ранее значения, сколько памяти надо на каких
бранчах -- https://www.altlinux.org/Apt/TroubleShooting .

Вместо APT::Cache-Limit поставьте такое достаточно большое значение
для APT::Cache-Start . Тогда динамическое наращивание памяти не
понадобится и не будет задействовано.
Comment 1 Ivan Zakharyaschev 2019-10-24 17:43:47 MSK
# Демонстрация причины проблемы: испорченные значения по указателям

Демонстрация проблемы в исходном коде (alt71), куда я перенёс
debugging сообщения из alt74 d6d78dacff5707c628ffffe1a98f76822ef06424
(thx darktemplar@) и добавил их вывод в начале Grow и в конце (а не
только в Allocate):

    diff --git a/apt/apt-pkg/contrib/mmap.cc b/apt/apt-pkg/contrib/mmap.cc
    index a3b06ccd6..e35c8a550 100644
    --- a/apt/apt-pkg/contrib/mmap.cc
    +++ b/apt/apt-pkg/contrib/mmap.cc
    @@ -211,6 +211,17 @@ DynamicMMap::~DynamicMMap()
        ftruncate(Fd->Fd(),EndOfFile);
     }  
     									/*}}}*/
    +void DynamicMMap::DebugPrintPools(const char * const MsgPrefix)
    +{
    +   for (size_t i = 0; i < PoolCount; ++i)
    +   {
    +      _error->Warning(_("%sPool %zu, item size: %lu, start: %lu, count: %lu"),
    +                      MsgPrefix,
    +                      i,
    +                      Pools[i].ItemSize, Pools[i].Start, Pools[i].Count);
    +    }
    +}
    +
     // DynamicMMap::RawAllocate - Allocate a raw chunk of unaligned space	/*{{{*/
     // ---------------------------------------------------------------------
     /* This allocates a block of memory aligned to the given size */
    @@ -265,6 +276,15 @@ std::experimental::optional<map_ptrloc> DynamicMMap::Allocate(unsigned long Item
        // No pool is allocated, use an unallocated one
        if (I == Pools + PoolCount)
        {
    +      static const bool debug_allocate = _config->FindB("Debug::DynamicMMap::Allocate", false);
    +
    +      if (debug_allocate)
    +      {
    +         _error->Warning(_("DynamicMMap::Allocate: allocating item of size %lu"), ItemSize);
    +
    +         DebugPrintPools("DynamicMMap::Allocate: ");
    +      }
    +
           // Woops, we ran out, the calling code should allocate more.
           if (Empty == 0)
           {
    @@ -356,6 +376,9 @@ bool DynamicMMap::Grow(unsigned long long size)
           Fd->Write(&C,sizeof(C));
        }
     
    +   if (debug_grow)
    +      DebugPrintPools("DynamicMMap::Grow: before remapping: ");
    +
        unsigned long const poolOffset = Pools - ((Pool*) Base);
     
        if (Fd != 0)
    @@ -396,6 +419,9 @@ bool DynamicMMap::Grow(unsigned long long size)
        Pools = (Pool*) Base + poolOffset;
        WorkSpace = newSize;
     
    +   if (debug_grow)
    +      DebugPrintPools("DynamicMMap::Grow: after remapping: ");
    +
        return true;
     }
     									/*}}}*/
    diff --git a/apt/apt-pkg/contrib/mmap.h b/apt/apt-pkg/contrib/mmap.h
    index bcbdaa129..be46f3d82 100644
    --- a/apt/apt-pkg/contrib/mmap.h
    +++ b/apt/apt-pkg/contrib/mmap.h
    @@ -106,6 +106,7 @@ class DynamicMMap : public MMap
        std::experimental::optional<map_ptrloc> WriteString(const char *String,unsigned long Len = std::numeric_limits<unsigned long>::max());
        inline std::experimental::optional<map_ptrloc> WriteString(const string &S) {return WriteString(S.c_str(),S.length());};
        void UsePools(Pool &P,unsigned int Count) {Pools = &P; PoolCount = Count;};
    +   void DebugPrintPools(const char *MsgPrefix);
     
        DynamicMMap(FileFd &F,unsigned long Flags,unsigned long long WorkSpace = 2*1024*1024,
                    unsigned long long Grow = 1024*1024, unsigned long long Limit = 0);
    diff --git a/apt/doc/apt.conf.5.sgml b/apt/doc/apt.conf.5.sgml
    index 0a72e454a..72fc0c369 100644
    --- a/apt/doc/apt.conf.5.sgml
    +++ b/apt/doc/apt.conf.5.sgml
    @@ -526,7 +526,7 @@ DPkg::Pre-Install-Pkgs {"/usr/sbin/dpkg-preconfigure --apt";};
        disable the inclusion of statfs data in CDROM IDs.
        </para><para>
        To debug issues related to dynamic memory allocation, an option
    -   <literal/Debug::DynamicMMap::Grow/ may be used.
    +   <literal/Debug::DynamicMMap::Grow/ and <literal/Debug::DynamicMMap::Allocate/ may be used.
        </para>
      </RefSect1>
      

Вот как это выглядит с task 23949 (указанным выше способом
воспроизведения проблемы):

    [imz@e801-1 ~]$ share_network=1 hsh-run ~/hasher-apt0-debug/ --mount=/dev/pts,/proc --root -- apt-get -o Debug::DynamicMMap::Grow=yes -o Debug::DynamicMMap::Allocate=yes update
    Get:1 ssh://imz@localhost sisyphus_e2k/release/latest/e2kv4 release [1544B]
    Get:2 ssh://imz@localhost sisyphus_e2k/release/latest/noarch release [1338B]
    Fetched 2882B in 0s (10.5kB/s)
    Hit ssh://imz@localhost sisyphus_e2k/release/latest/e2kv4/classic pkglist
    Hit ssh://imz@localhost sisyphus_e2k/release/latest/e2kv4/classic release
    Hit ssh://imz@localhost sisyphus_e2k/release/latest/e2kv4/debuginfo pkglist
    Hit ssh://imz@localhost sisyphus_e2k/release/latest/e2kv4/debuginfo release
    Hit ssh://imz@localhost sisyphus_e2k/release/latest/noarch/classic pkglist
    Hit ssh://imz@localhost sisyphus_e2k/release/latest/noarch/classic release
    Reading Package Lists...
    W: DynamicMMap::Allocate: allocating item of size 72
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 4, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 5, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 6, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: allocating item of size 8
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 4, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 5, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262584, count: 283
    W: DynamicMMap::Allocate: allocating item of size 48
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 4, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283088, count: 2558
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262584, count: 283
    W: DynamicMMap::Allocate: allocating item of size 64
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 4, item size: 48, start: 303648, count: 425
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283088, count: 2558
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262584, count: 283
    W: DynamicMMap::Allocate: allocating item of size 28
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 64, start: 324160, count: 319
    W: DynamicMMap::Allocate: Pool 4, item size: 48, start: 303648, count: 425
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283104, count: 2556
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262584, count: 283
    W: DynamicMMap::Allocate: allocating item of size 20
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 28, start: 345240, count: 709
    W: DynamicMMap::Allocate: Pool 3, item size: 64, start: 324160, count: 319
    W: DynamicMMap::Allocate: Pool 4, item size: 48, start: 304704, count: 403
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283104, count: 2556
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262584, count: 283
    W: DynamicMMap::Allocate: allocating item of size 16
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 20, start: 365840, count: 1023
    W: DynamicMMap::Allocate: Pool 2, item size: 28, start: 345240, count: 709
    W: DynamicMMap::Allocate: Pool 3, item size: 64, start: 324160, count: 319
    W: DynamicMMap::Allocate: Pool 4, item size: 48, start: 304704, count: 403
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283104, count: 2556
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262584, count: 283
    W: DynamicMMap::Grow: before remapping: Pool 0, item size: 16, start: 25133312, count: 1238
    W: DynamicMMap::Grow: before remapping: Pool 1, item size: 20, start: 25091160, count: 860
    W: DynamicMMap::Grow: before remapping: Pool 2, item size: 28, start: 25054680, count: 731
    W: DynamicMMap::Grow: before remapping: Pool 3, item size: 64, start: 25114880, count: 277
    W: DynamicMMap::Grow: before remapping: Pool 4, item size: 48, start: 25076976, count: 54
    W: DynamicMMap::Grow: before remapping: Pool 5, item size: 8, start: 283984, count: 2446
    W: DynamicMMap::Grow: before remapping: Pool 6, item size: 72, start: 262728, count: 281
    W: DynamicMMap::Grow: mremap from 25165824 to 26214400, result: Success
    W: DynamicMMap::Grow: after remapping: Pool 0, item size: 1127162627490072, start: 65445, count: 16
    W: DynamicMMap::Grow: after remapping: Pool 1, item size: 25133312, start: 1238, count: 20
    W: DynamicMMap::Grow: after remapping: Pool 2, item size: 25091160, start: 860, count: 28
    W: DynamicMMap::Grow: after remapping: Pool 3, item size: 25054680, start: 731, count: 64
    W: DynamicMMap::Grow: after remapping: Pool 4, item size: 25114880, start: 277, count: 48
    W: DynamicMMap::Grow: after remapping: Pool 5, item size: 25076976, start: 54, count: 8
    W: DynamicMMap::Grow: after remapping: Pool 6, item size: 283984, start: 2446, count: 72
    W: DynamicMMap::Allocate: allocating item of size 28
    W: DynamicMMap::Allocate: Pool 0, item size: 1127162627490072, start: 65445, count: 16
    W: DynamicMMap::Allocate: Pool 1, item size: 25133312, start: 1238, count: 20
    W: DynamicMMap::Allocate: Pool 2, item size: 25091160, start: 25156488, count: 27
    W: DynamicMMap::Allocate: Pool 3, item size: 25054680, start: 731, count: 64
    W: DynamicMMap::Allocate: Pool 4, item size: 25114880, start: 277, count: 48
    W: DynamicMMap::Allocate: Pool 5, item size: 25076976, start: 54, count: 8
    W: DynamicMMap::Allocate: Pool 6, item size: 283984, start: 2446, count: 72
    E: Ran out of allocation pools
    W: DynamicMMap::Allocate: allocating item of size 20
    W: DynamicMMap::Allocate: Pool 0, item size: 1127162627490072, start: 65445, count: 16
    W: DynamicMMap::Allocate: Pool 1, item size: 25133312, start: 1238, count: 20
    W: DynamicMMap::Allocate: Pool 2, item size: 25091160, start: 25156488, count: 27
    W: DynamicMMap::Allocate: Pool 3, item size: 25054680, start: 731, count: 64
    W: DynamicMMap::Allocate: Pool 4, item size: 25114880, start: 277, count: 48
    W: DynamicMMap::Allocate: Pool 5, item size: 25076976, start: 54, count: 8
    W: DynamicMMap::Allocate: Pool 6, item size: 283984, start: 2446, count: 72
    E: Ran out of allocation pools
    E: Error occured while processing docx2txt (NewVersion2)
    E: Problem with MergeList /var/lib/apt/lists/imz%40localhost_girar%5frepo_sisyphus%5fe2k_release_latest_noarch_base_pkglist.classic
    E: The package lists or status file could not be parsed or opened.
    [imz@e801-1 ~]$ 

Тут видно, что испортились значения в массиве Pools в конце Grow()
("DynamicMMap::Grow: after remapping").
Comment 2 Ivan Zakharyaschev 2019-10-24 17:44:06 MSK
# Демонстрация той же проблемы при использовании realloc вместо mremap

При использовании realloc вместо mremap то же самое.

Проверить это довольно просто, например, благодаря тому, что от
пользователя-не-root apt не может писать в свои файлы с бинарным кешем
и не использует mmap. Чтобы начать его конструировать в памяти,
необязательно вызывать именно `apt-get update`, как в примерах выше,
можно, например, `apt-cache search`. Учитывая это, пользуемся
описанным выше способом тестирования в том же хешере с task 23949:

Вот так это выглядит с realloc:

    [imz@e801-1 ~]$ share_network=1 hsh-run ~/hasher-apt0-debug/ --mount=/dev/pts,/proc -- apt-cache -o Debug::DynamicMMap::Grow=yes -o Debug::DynamicMMap::Allocate=yes search kernel-image
    W: DynamicMMap::Allocate: allocating item of size 72
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 4, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 5, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 6, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: allocating item of size 8
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 4, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 5, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262584, count: 283
    W: DynamicMMap::Allocate: allocating item of size 48
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 4, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283088, count: 2558
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262584, count: 283
    W: DynamicMMap::Allocate: allocating item of size 64
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 4, item size: 48, start: 303648, count: 425
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283088, count: 2558
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262584, count: 283
    W: DynamicMMap::Allocate: allocating item of size 28
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 64, start: 324160, count: 319
    W: DynamicMMap::Allocate: Pool 4, item size: 48, start: 303648, count: 425
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283104, count: 2556
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262584, count: 283
    W: DynamicMMap::Allocate: allocating item of size 20
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 28, start: 345240, count: 709
    W: DynamicMMap::Allocate: Pool 3, item size: 64, start: 324160, count: 319
    W: DynamicMMap::Allocate: Pool 4, item size: 48, start: 304704, count: 403
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283104, count: 2556
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262584, count: 283
    W: DynamicMMap::Allocate: allocating item of size 16
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 20, start: 365840, count: 1023
    W: DynamicMMap::Allocate: Pool 2, item size: 28, start: 345240, count: 709
    W: DynamicMMap::Allocate: Pool 3, item size: 64, start: 324160, count: 319
    W: DynamicMMap::Allocate: Pool 4, item size: 48, start: 304704, count: 403
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283104, count: 2556
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262584, count: 283
    W: DynamicMMap::Grow: before remapping: Pool 0, item size: 16, start: 23575664, count: 44
    W: DynamicMMap::Grow: before remapping: Pool 1, item size: 20, start: 25028280, count: 1024
    W: DynamicMMap::Grow: before remapping: Pool 2, item size: 28, start: 25074952, count: 132
    W: DynamicMMap::Grow: before remapping: Pool 3, item size: 64, start: 24946496, count: 43
    W: DynamicMMap::Grow: before remapping: Pool 4, item size: 48, start: 25136496, count: 243
    W: DynamicMMap::Grow: before remapping: Pool 5, item size: 8, start: 283984, count: 2446
    W: DynamicMMap::Grow: before remapping: Pool 6, item size: 72, start: 262728, count: 281
    W: DynamicMMap::Grow: realloc from 25165824 to 26214400, result: Success
    W: DynamicMMap::Grow: after remapping: Pool 0, item size: 1127162627490072, start: 65445, count: 16
    W: DynamicMMap::Grow: after remapping: Pool 1, item size: 23575664, start: 44, count: 20
    W: DynamicMMap::Grow: after remapping: Pool 2, item size: 25028280, start: 1024, count: 28
    W: DynamicMMap::Grow: after remapping: Pool 3, item size: 25074952, start: 132, count: 64
    W: DynamicMMap::Grow: after remapping: Pool 4, item size: 24946496, start: 43, count: 48
    W: DynamicMMap::Grow: after remapping: Pool 5, item size: 25136496, start: 243, count: 8
    W: DynamicMMap::Grow: after remapping: Pool 6, item size: 283984, start: 2446, count: 72
    W: DynamicMMap::Allocate: allocating item of size 48
    W: DynamicMMap::Allocate: Pool 0, item size: 1127162627490072, start: 65445, count: 16
    W: DynamicMMap::Allocate: Pool 1, item size: 23575664, start: 44, count: 20
    W: DynamicMMap::Allocate: Pool 2, item size: 25153580, start: 1023, count: 28
    W: DynamicMMap::Allocate: Pool 3, item size: 25074952, start: 132, count: 64
    W: DynamicMMap::Allocate: Pool 4, item size: 24946496, start: 43, count: 48
    W: DynamicMMap::Allocate: Pool 5, item size: 25136496, start: 243, count: 8
    W: DynamicMMap::Allocate: Pool 6, item size: 283984, start: 2446, count: 72
    E: Ran out of allocation pools
    E: Error occured while processing dbsake (NewVersion2)
    E: Problem with MergeList /var/lib/apt/lists/imz%40localhost_girar%5frepo_sisyphus%5fe2k_release_latest_noarch_base_pkglist.classic
    [imz@e801-1 ~]$ 

Тут сообщение выглядит так

    W: DynamicMMap::Grow: realloc from 25165824 to 26214400, result: Success

а не так, как в случае mremap в предыдущем примере:

    W: DynamicMMap::Grow: mremap from 25165824 to 26214400, result: Success
Comment 3 Ivan Zakharyaschev 2019-10-24 17:44:27 MSK
# Исправление перевычисления указателя Pools после перемещения куска памяти

Когда узнал об этой проблеме и понял, что причиной должны быть
неправильные значения в `Pools`, я попробовал посмотреть, что будет,
если исправить вычисление нового указателя Pools после перемещения
(mremap или realloc) используемого APT-ом куска памяти: для получения
нового значения указателя `Pools` откладывалось такое же смещение
(`poolOffset`) относительно начала используемого куска памяти (`Base`),
как было раньше (относительно старого `Base`), но вычислялось это смещение
(`poolOffset`) в единицах размера структуры Pool, хотя по сути разница
между адресами начала куска памяти и `Pools` могла быть некратна размеру
структуры Pool!

(Т.е. невозможно представить, как вычисление смещения должно работать,
если аргументы вычитания несравнимы по модулю размера структуры, а
такое вычитание здесь использовалось в коде. Стандарт С и C++ говорит
просто: значение не определено, если указатели указывают не на
элементы одного массива, т.е. по стандарту накладывается ещё более
жёсткое ограничение, чем практическая осмысленность.)

    5ba996927 fix the calculation of an offset pointer after realloc by relying on char* arith
    diff --git a/apt/apt-pkg/contrib/mmap.cc b/apt/apt-pkg/contrib/mmap.cc
    index 331339a87..b4a9f0384 100644
    --- a/apt/apt-pkg/contrib/mmap.cc
    +++ b/apt/apt-pkg/contrib/mmap.cc
    @@ -38,7 +38,6 @@
     #include <unistd.h>
     #include <fcntl.h>
     #include <cstring>
    -   									/*}}}*/
     
     // MMap::MMap - Constructor						/*{{{*/
     // ---------------------------------------------------------------------
    @@ -356,7 +355,7 @@ bool DynamicMMap::Grow(unsigned long long size)
           Fd->Write(&C,sizeof(C));
        }
     
    -   unsigned long const poolOffset = Pools - ((Pool*) Base);
    +   std::ptrdiff_t const poolOffset = reinterpret_cast<char *>(Pools) - static_cast<char *>(Base);
     
        if (Fd != 0)
        {
    @@ -393,7 +392,7 @@ bool DynamicMMap::Grow(unsigned long long size)
           memset((char*)Base + WorkSpace, 0, newSize - WorkSpace);
        }
     
    -   Pools = (Pool*) Base + poolOffset;
    +   Pools = reinterpret_cast<decltype(Pools)>(static_cast<char *>(Base) + poolOffset);
        WorkSpace = newSize;
     
        return true;

Если бы можно было быть уверенным, что адреса `Base` и `Pools` всегда
выравнены (aligned) по размеру структуры Pool (т.е. кратны ему), то
это могло бы работать правильно. (Эта мысль предлагает ещё один путь
исправления этого вычисления и других аналогичных мест в коде. Более
экзотический вариант, когда оно могло бы работать: когда оба адреса
сравнимы по модулю этого размера, т.е. остаток один и тот же -- я не
буду всерьёз рассматривать. В таком случае приведение `Base` к типу
указателя на структуру Pool, возможно, не портило бы значение, но тем
не менее выглядит неприятно.)

## Другие перевычисления указателей

Также озабоченность сразу вызвали все аналогичные места, где
перевычисляются указатели после перемещения куска памяти (в функциях
`ReMap()`, например). Там немного другой способ вычисления: каждый из
них смещается на разницу `oldMap - newMap` (т.е. на разницу между
старым и новым началом подкуска куска памяти, а не как в предыдущем
месте кода про `Pools`, где вычисление идёт через отступ от старого
начала к старому интересному месту в памяти, а потом такой же отступ
хотят отложить от нового начала). Но проблема та же: эти
адреса (`oldMap` и `newMap`) рассматриваются как указатели на
определённую структуру и эта разность будет иметь смысл, если она в
реальности кратна размеру этой структуры.
См. apt-pkg/cacheiterators.h:

     void ReMap(void const * const oldMap, void const * const newMap)
     {
        if (Owner == 0 || Pkg == 0)
           return;
        Pkg += static_cast<Package const *>(newMap) - static_cast<Package const *>(oldMap);
     }

Хочу исправить и запрятать эту операцию, например, в шаблонную функцию.

(Изначально, при первом знакомстве с этим местом
https://bugzilla.altlinux.org/show_bug.cgi?id=30482#c8, у меня такое
не чётко сформулированное желание
возникло ради ужесточения проверки компилятором правильности выражения
с помощью типов: сейчас разность (ptrdiff_t) не несёт информацию о
том, в терминах размера какой структуры она посчитана, поэтому
представьте себе, что тип указателя (`Pkg`) поменялся в коде в будущем
и не будет совпадать с явно записанным в выражении Package* (или в
этом явном указании типа попросту программист допустил ошибку), а
вычисление продолжит производиться в терминах размера старой структуры
(Package), и компилятор может это молча не заметить (если оба типа
есть в новом коде). Так что я хотел по крайней мере заменить явную
запись типа на decltype(Pkg).)

Теперь, обнаружив неправильность и собираясь переписать это вычисление
в более правильном виде (в духе исправления `Pools` выше), полагаю,
что запрятывание этого вычисления в шаблон ещё хорошо и тем, что
использование "нехороших" reinterpret_cast не будет рассеяно по коду,
а будет локализовано внутри одной "надёжной" перепроверенной функции,
внешне имеющий обычный интерфейс в рамках обычной системы типов. Т.е.
при изучении остального кода не надо будет обращать лишнее внимание на
"нехорошие", "опасные" конструкции.

Такой же подход можно было бы применить и для вычисления `Pools` после
перемещения памяти.)

(Вряд ли в этом месте некратной разницы не бывает. Хоть это и
не `Base - oldBase`, а `newMap - oldMap`, но при перемещение всего
куска памяти на смещение некратное размеру структуры повлечёт и такое
же смещение всего, что внутри.)

## Озабоченность из-за alignment данных после перемещения

А также озабоченность вызывает (но я в этом не уверен, мало обдумывал)
то, что все эти даныые могут оказаться после перемещения неудачно
выравнены (aligned) в памяти, а некоторые платформы чувствительны к
unaligned access к данным. (Платформы скорее не наши: старый arm,
mips, ia64, но всё же нехорошо об этом не подумать; там это может быть
ошибка или замедление в зависмости от режима работы.)

## Результат тестирования исправления вычисления Pools

Результат тестирования (с debugging выводом, сделанным мной тогда на
коленке, аналогичным тому, что появилось в alt74
d6d78dacff5707c628ffffe1a98f76822ef06424 -- в итоге, конечно, я
предпочту вариант d6d78dacff5707c628ffffe1a98f76822ef06424 как более
тщательный):

Вот как это выглядит с task 23727 (указанным выше способом
воспроизведения проблемы):

    [imz@e801-1 ~]$ share_network=1 hsh-run ~/hasher-apt1-correct-offset/ --mount=/dev/pts,/proc --root -- apt-get -o Debug::DynamicMMap::Grow=yes -o Debug::DynamicMMap::Allocate=yes update
    Get:1 ssh://imz@localhost sisyphus_e2k/release/latest/e2kv4 release [1544B]
    Get:2 ssh://imz@localhost sisyphus_e2k/release/latest/noarch release [1338B]
    Fetched 2882B in 0s (8853B/s)
    Get:1 ssh://imz@localhost sisyphus_e2k/release/latest/e2kv4/classic pkglist [10.2MB]
    Get:2 ssh://imz@localhost sisyphus_e2k/release/latest/e2kv4/classic release [154B]
    Get:3 ssh://imz@localhost sisyphus_e2k/release/latest/e2kv4/debuginfo pkglist [2176kB]
    Get:4 ssh://imz@localhost sisyphus_e2k/release/latest/e2kv4/debuginfo release [156B]
    Get:5 ssh://imz@localhost sisyphus_e2k/release/latest/noarch/classic pkglist [3365kB]
    Get:6 ssh://imz@localhost sisyphus_e2k/release/latest/noarch/classic release [155B]
    Fetched 15.7MB in 4s (3363kB/s)
    Reading Package Lists...
    W: DynamicMMap::Grow: old Pools[*]: 16
    W: DynamicMMap::Grow: old Pools[*]: 20
    W: DynamicMMap::Grow: old Pools[*]: 28
    W: DynamicMMap::Grow: old Pools[*]: 64
    W: DynamicMMap::Grow: old Pools[*]: 48
    W: DynamicMMap::Grow: old Pools[*]: 8
    W: DynamicMMap::Grow: old Pools[*]: 72
    W: DynamicMMap::Grow: mremap from 25165824 to 26214400, result: Success
    W: DynamicMMap::Grow: new Pools[*]: 16
    W: DynamicMMap::Grow: new Pools[*]: 20
    W: DynamicMMap::Grow: new Pools[*]: 28
    W: DynamicMMap::Grow: new Pools[*]: 64
    W: DynamicMMap::Grow: new Pools[*]: 48
    W: DynamicMMap::Grow: new Pools[*]: 8
    W: DynamicMMap::Grow: new Pools[*]: 72
    W: DynamicMMap::Allocate: current Pools[*]: 16
    W: DynamicMMap::Allocate: current Pools[*]: 20
    W: DynamicMMap::Allocate: current Pools[*]: 27
    W: DynamicMMap::Allocate: current Pools[*]: 64
    W: DynamicMMap::Allocate: current Pools[*]: 48
    W: DynamicMMap::Allocate: current Pools[*]: 8
    W: DynamicMMap::Allocate: current Pools[*]: 72
    E: Ran out of allocation pools (7); needing one for size 28l
    E: Error occured while processing docx2txt (UsePackage3)
    E: Problem with MergeList /var/lib/apt/lists/imz%40localhost_girar%5frepo_sisyphus%5fe2k_release_latest_noarch_base_pkglist.classic
    E: The package lists or status file could not be parsed or opened.
    [imz@e801-1 ~]$ 

Примечательно, что значения `ItemSize` после моего исправления больше
не портятся сразу после перемещения куска памяти (как я и ожидал), но
всё равно, к сожалению, портятся где-то в промежутке до следующего
вызова Allocate(): третье значение 28 поменялось на 27.

Предполагаю, что какие-то ещё указатели вычислились неправильно, чтобы
учесть перемещения куска памяти (по похожей причине, что я исправлял
для `Pools`), и по ним был доступ (с записью) к памяти, хранящей это в
итоге испорченное значение. Так что это в общем-то неудивительно (что
где-то что-то ещё портится), потому что такой ошибочный способ
перевычисления указателей действительно был замечен во многих местах в
apt.

## Сравнение с другими платформами, где работает

Кстати,

* в современном Debian-овском apt-е эти вычисления делаются
таким же неприятным способом.
* Также в apt-е от МЦСТ для e2k (т.е. том же Debian-овском) на эту
тему тоже ничего не поправлено.
* На x86\_64 тем временем у нас этот код пока тоже работал нормально.

Это вероятно значит, что как-то повезло, что там отступы и
перерасположения в памяти (mremap, realloc) делаются удачно.
("Заслуга" gcc, glibc или того, что какие-то типы другого размера на
x86\_64, чем на e2k?..)
Comment 4 Ivan Zakharyaschev 2019-10-24 17:44:43 MSK
# Исправление с помощью выравнивание

Чтобы исправить ситуацию, darktemplar@ указал выравнивание для
структуры Pool:

    commit f67c64cc442aa024ee859e047fa68fcc49544c49
    Author: Aleksei Nikiforov <darktemplar@altlinux.org>
    Date:   Mon Sep 16 15:37:00 2019 +0300
    
        Improve alignment of DynamicMMap::Pool structure
        
        This change should fix pointer arithmetic issues for e2k.
        Also use special ptrdiff_t type.
    
    diff --git a/apt/apt-pkg/contrib/mmap.cc b/apt/apt-pkg/contrib/mmap.cc
    index cf01be95a..ea2aded9f 100644
    --- a/apt/apt-pkg/contrib/mmap.cc
    +++ b/apt/apt-pkg/contrib/mmap.cc
    @@ -38,6 +38,7 @@
     #include <unistd.h>
     #include <fcntl.h>
     #include <cstring>
    +#include <stddef.h>
        									/*}}}*/
     
     // MMap::MMap - Constructor						/*{{{*/
    @@ -371,7 +372,7 @@ bool DynamicMMap::Grow(unsigned long long size)
           Fd->Write(&C,sizeof(C));
        }
     
    -   unsigned long const poolOffset = Pools - ((Pool*) Base);
    +   ptrdiff_t const poolOffset = Pools - ((Pool*) Base);
     
        if (Fd != 0)
        {
    diff --git a/apt/apt-pkg/contrib/mmap.h b/apt/apt-pkg/contrib/mmap.h
    index bcbdaa129..f49492cf1 100644
    --- a/apt/apt-pkg/contrib/mmap.h
    +++ b/apt/apt-pkg/contrib/mmap.h
    @@ -80,7 +80,7 @@ class DynamicMMap : public MMap
        public:
        
        // This is the allocation pool structure
    -   struct Pool
    +   struct alignas(alignof(unsigned long) * 4) Pool
        {
           unsigned long ItemSize;
           unsigned long Start;


Вот как это выглядит с этим изменением в task 23751 (указанным выше
способом воспроизведения проблемы):

    [imz@e801-1 ~]$ share_network=1 hsh-run ~/hasher-apt2-correct-align-incomplete/ --mount=/dev/pts,/proc --root -- apt-get -o Debug::DynamicMMap::Grow=yes -o Debug::DynamicMMap::Allocate=yes update
    Get:1 ssh://localhost sisyphus_e2k/release/latest/e2kv4 release [1544B]
    Get:2 ssh://localhost sisyphus_e2k/release/latest/noarch release [1338B]
    Fetched 2882B in 0s (8610B/s)
    Get:1 ssh://localhost sisyphus_e2k/release/latest/e2kv4/classic pkglist [10.2MB]
    Get:2 ssh://localhost sisyphus_e2k/release/latest/e2kv4/classic release [154B]
    Get:3 ssh://localhost sisyphus_e2k/release/latest/e2kv4/debuginfo pkglist [2177kB]
    Get:4 ssh://localhost sisyphus_e2k/release/latest/e2kv4/debuginfo release [156B]
    Get:5 ssh://localhost sisyphus_e2k/release/latest/noarch/classic pkglist [3365kB]
    Get:6 ssh://localhost sisyphus_e2k/release/latest/noarch/classic release [155B]
    Fetched 15.7MB in 4s (3538kB/s)
    Reading Package Lists...
    Building Dependency Tree...
    W: DynamicMMap::Allocate: allocating item of size 72
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 4, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 5, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 6, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: allocating item of size 8
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 4, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 5, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262656, count: 283
    W: DynamicMMap::Allocate: allocating item of size 48
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 4, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283152, count: 2558
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262656, count: 283
    W: DynamicMMap::Allocate: allocating item of size 64
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 4, item size: 48, start: 303696, count: 425
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283152, count: 2558
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262656, count: 283
    W: DynamicMMap::Allocate: allocating item of size 28
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 64, start: 324224, count: 319
    W: DynamicMMap::Allocate: Pool 4, item size: 48, start: 303696, count: 425
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283168, count: 2556
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262656, count: 283
    W: DynamicMMap::Allocate: allocating item of size 20
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 28, start: 345324, count: 709
    W: DynamicMMap::Allocate: Pool 3, item size: 64, start: 324224, count: 319
    W: DynamicMMap::Allocate: Pool 4, item size: 48, start: 304752, count: 403
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283168, count: 2556
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262656, count: 283
    W: DynamicMMap::Allocate: allocating item of size 16
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 20, start: 365920, count: 1023
    W: DynamicMMap::Allocate: Pool 2, item size: 28, start: 345324, count: 709
    W: DynamicMMap::Allocate: Pool 3, item size: 64, start: 324224, count: 319
    W: DynamicMMap::Allocate: Pool 4, item size: 48, start: 304752, count: 403
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283168, count: 2556
    W: DynamicMMap::Allocate: Pool 6, item size: 72, start: 262656, count: 283
    W: DynamicMMap::Grow: mremap from 25165824 to 26214400, result: Success
    W: DynamicMMap::Grow: mremap from 26214400 to 27262976, result: Success
    W: DynamicMMap::Grow: mremap from 27262976 to 28311552, result: Success
    W: DynamicMMap::Grow: mremap from 28311552 to 29360128, result: Success
    W: DynamicMMap::Grow: mremap from 29360128 to 30408704, result: Success
    W: DynamicMMap::Grow: mremap from 30408704 to 31457280, result: Success
    W: DynamicMMap::Grow: mremap from 31457280 to 32505856, result: Success
    W: DynamicMMap::Grow: mremap from 32505856 to 33554432, result: Success
    W: DynamicMMap::Grow: mremap from 33554432 to 34603008, result: Success
    W: DynamicMMap::Grow: mremap from 34603008 to 35651584, result: Success
    W: DynamicMMap::Grow: mremap from 35651584 to 36700160, result: Success
    W: DynamicMMap::Grow: mremap from 36700160 to 37748736, result: Success
    W: You may want to run apt-get update to correct these problems
    [imz@e801-1 ~]$ 

(А изменение значения поля `Start` для некоторых элементов в этом
примере -- не проблема? Нет, вот это единственное место, где оно
меняется в DynamicMMap::Allocate() в mmap.cc:

    I->Count--;
    I->Start += ItemSize;  
    return std::experimental::optional<map_ptrloc>(Result/ItemSize);

После забирания памяти из pool-а и присоединения к массиву в конец,
его начало сдвигается вперёд.)
Comment 5 Ivan Zakharyaschev 2019-10-24 17:45:00 MSK
# Выравнивание других структур

darktemplar@ распространил этот способ исправления (с помощью
выравнивания) на другие структуры (в alt74):

    commit 9e6dc9a082c2e4f1b420ff57734a782b358ce317
    Author: Aleksei Nikiforov <darktemplar@altlinux.org>
    Date:   Mon Sep 16 15:37:00 2019 +0300
    
        Improve alignment of structures moved on memory reallocation
        
        This change should fix pointer arithmetic issues for e2k.
        Also use special ptrdiff_t type.
    
    diff --git a/apt/apt-pkg/Makefile.am b/apt/apt-pkg/Makefile.am
    index 4c0d234f6..44610788d 100644
    --- a/apt/apt-pkg/Makefile.am
    +++ b/apt/apt-pkg/Makefile.am
    @@ -105,6 +105,7 @@ libapt_pkg_la_SOURCES = \
     	tagfile.h \
     	update.cc \
     	update.h \
    +	utils.h \
     	version.cc \
     	version.h \
     	versionmatch.cc \
    diff --git a/apt/apt-pkg/contrib/mmap.cc b/apt/apt-pkg/contrib/mmap.cc
    index cf01be95a..ea2aded9f 100644
    --- a/apt/apt-pkg/contrib/mmap.cc
    +++ b/apt/apt-pkg/contrib/mmap.cc
    @@ -38,6 +38,7 @@
     #include <unistd.h>
     #include <fcntl.h>
     #include <cstring>
    +#include <stddef.h>
        									/*}}}*/
     
     // MMap::MMap - Constructor						/*{{{*/
    @@ -371,7 +372,7 @@ bool DynamicMMap::Grow(unsigned long long size)
           Fd->Write(&C,sizeof(C));
        }
     
    -   unsigned long const poolOffset = Pools - ((Pool*) Base);
    +   ptrdiff_t const poolOffset = Pools - ((Pool*) Base);
     
        if (Fd != 0)
        {
    diff --git a/apt/apt-pkg/contrib/mmap.h b/apt/apt-pkg/contrib/mmap.h
    index bcbdaa129..439e4faa4 100644
    --- a/apt/apt-pkg/contrib/mmap.h
    +++ b/apt/apt-pkg/contrib/mmap.h
    @@ -35,6 +35,7 @@
     #include <sys/mman.h>
     
     #include <apt-pkg/fileutl.h>
    +#include <apt-pkg/utils.h>
     
     using std::string;
     
    @@ -80,7 +81,7 @@ class DynamicMMap : public MMap
        public:
        
        // This is the allocation pool structure
    -   struct Pool
    +   struct alignas(get_minimal_power_of_2(sizeof(unsigned long) * 3)) Pool
        {
           unsigned long ItemSize;
           unsigned long Start;
    diff --git a/apt/apt-pkg/pkgcache.h b/apt/apt-pkg/pkgcache.h
    index 56fc89db7..324ece5a8 100644
    --- a/apt/apt-pkg/pkgcache.h
    +++ b/apt/apt-pkg/pkgcache.h
    @@ -25,6 +25,7 @@
     #include <string>
     #include <time.h>
     #include <apt-pkg/mmap.h>
    +#include <apt-pkg/utils.h>
     
     using std::string;
         
    @@ -209,7 +210,7 @@ struct pkgCache::Header
        Header();
     };
     
    -struct pkgCache::Package
    +struct alignas(get_minimal_power_of_2(sizeof(map_ptrloc) * 7 + sizeof(unsigned char) * 3 + sizeof(unsigned int) + sizeof(unsigned long))) pkgCache::Package
     {
        // Pointers
        map_ptrloc Name;              // Stringtable
    @@ -231,7 +232,7 @@ struct pkgCache::Package
        unsigned long Flags;
     };
     
    -struct pkgCache::PackageFile
    +struct alignas(get_minimal_power_of_2(sizeof(map_ptrloc) * 10 + sizeof(unsigned long long) + sizeof(unsigned long) + sizeof(unsigned short) + sizeof(time_t))) pkgCache::PackageFile
     {
        // Names
        map_ptrloc FileName;        // Stringtable
    @@ -252,7 +253,7 @@ struct pkgCache::PackageFile
        time_t mtime;                  // Modification time for the file
     };
     
    -struct pkgCache::VerFile
    +struct alignas(get_minimal_power_of_2(sizeof(map_ptrloc) * 3 + sizeof(unsigned short))) pkgCache::VerFile
     {
        map_ptrloc File;           // PackageFile
        map_ptrloc NextFile;       // PkgVerFile
    @@ -260,7 +261,7 @@ struct pkgCache::VerFile
        unsigned short Size;
     };
     
    -struct pkgCache::Version
    +struct alignas(get_minimal_power_of_2(sizeof(map_ptrloc) * 8 + sizeof(unsigned long long) * 2 + sizeof(unsigned int) * 2 + sizeof(unsigned char))) pkgCache::Version
     {
        map_ptrloc VerStr;            // Stringtable
        map_ptrloc Section;           // StringTable (StringItem)
    @@ -284,7 +285,7 @@ struct pkgCache::Version
        unsigned char Priority;
     };
     
    -struct pkgCache::Dependency
    +struct alignas(get_minimal_power_of_2(sizeof(map_ptrloc) * 6 + sizeof(unsigned char) * 2)) pkgCache::Dependency
     {
        map_ptrloc Version;         // Stringtable
        map_ptrloc Package;         // Package
    @@ -298,7 +299,7 @@ struct pkgCache::Dependency
        unsigned char CompareOp;
     };
     
    -struct pkgCache::Provides
    +struct alignas(get_minimal_power_of_2(sizeof(map_ptrloc) * 6)) pkgCache::Provides
     {
        map_ptrloc ParentPkg;        // Pacakge
        map_ptrloc Version;          // Version
    @@ -307,7 +308,7 @@ struct pkgCache::Provides
        map_ptrloc NextPkgProv;      // Provides
     };
     
    -struct pkgCache::StringItem
    +struct alignas(get_minimal_power_of_2(sizeof(map_ptrloc) * 2)) pkgCache::StringItem
     {
        map_ptrloc String;        // Stringtable
        map_ptrloc NextItem;      // StringItem
    diff --git a/apt/apt-pkg/utils.h b/apt/apt-pkg/utils.h
    new file mode 100644
    index 000000000..08f7f18d2
    --- /dev/null
    +++ b/apt/apt-pkg/utils.h
    @@ -0,0 +1,17 @@
    +#ifndef APT_EXTRA_UTILS_H
    +#define APT_EXTRA_UTILS_H
    +
    +#include <stddef.h>
    +
    +template <size_t N>
    +constexpr size_t get_minimal_power_of_2_helper(size_t size)
    +{
    +   return (size <= N) ? N : get_minimal_power_of_2_helper<N*2>(size);
    +}
    +
    +constexpr size_t get_minimal_power_of_2(size_t size)
    +{
    +   return get_minimal_power_of_2_helper<1>(size);
    +}
    +
    +#endif /* APT_EXTRA_UTILS_H */

Вот как это выглядит с этим улучшением в task 23973 (указанным выше
способом воспроизведения проблемы):

    [imz@e801-1 ~]$ share_network=1 hsh-run ~/hasher-apt3-correct-align-complete/ --mount=/dev/pts,/proc --root -- apt-get -o Debug::DynamicMMap::Grow=yes -o Debug::DynamicMMap::Allocate=yes update
    Get:1 ssh://localhost sisyphus_e2k/release/latest/e2kv4 release [1544B]
    Get:2 ssh://localhost sisyphus_e2k/release/latest/noarch release [1338B]
    Fetched 2882B in 0s (8607B/s)
    Get:1 ssh://localhost sisyphus_e2k/release/latest/e2kv4/classic pkglist [10.2MB]
    Get:2 ssh://localhost sisyphus_e2k/release/latest/e2kv4/classic release [154B]
    Get:3 ssh://localhost sisyphus_e2k/release/latest/e2kv4/debuginfo pkglist [2177kB]
    Get:4 ssh://localhost sisyphus_e2k/release/latest/e2kv4/debuginfo release [156B]
    Get:5 ssh://localhost sisyphus_e2k/release/latest/noarch/classic pkglist [3365kB]
    Get:6 ssh://localhost sisyphus_e2k/release/latest/noarch/classic release [155B]
    Fetched 15.7MB in 4s (3546kB/s)
    Reading Package Lists...
    Building Dependency Tree...
    W: DynamicMMap::Allocate: allocating item of size 128
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 4, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 5, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 6, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: allocating item of size 8
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 4, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 5, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 6, item size: 128, start: 262656, count: 159
    W: DynamicMMap::Allocate: allocating item of size 64
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 4, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283128, count: 2558
    W: DynamicMMap::Allocate: Pool 6, item size: 128, start: 262656, count: 159
    W: DynamicMMap::Allocate: allocating item of size 32
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 4, item size: 64, start: 303808, count: 318
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283144, count: 2556
    W: DynamicMMap::Allocate: Pool 6, item size: 128, start: 262656, count: 159
    W: DynamicMMap::Allocate: allocating item of size 16
    W: DynamicMMap::Allocate: Pool 0, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 1, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 2, item size: 0, start: 0, count: 0
    W: DynamicMMap::Allocate: Pool 3, item size: 32, start: 324960, count: 617
    W: DynamicMMap::Allocate: Pool 4, item size: 64, start: 305216, count: 296
    W: DynamicMMap::Allocate: Pool 5, item size: 8, start: 283144, count: 2556
    W: DynamicMMap::Allocate: Pool 6, item size: 128, start: 262656, count: 159
    W: DynamicMMap::Grow: mremap from 25165824 to 26214400, result: Success
    W: DynamicMMap::Grow: mremap from 26214400 to 27262976, result: Success
    W: DynamicMMap::Grow: mremap from 27262976 to 28311552, result: Success
    W: DynamicMMap::Grow: mremap from 28311552 to 29360128, result: Success
    W: DynamicMMap::Grow: mremap from 29360128 to 30408704, result: Success
    W: DynamicMMap::Grow: mremap from 30408704 to 31457280, result: Success
    W: DynamicMMap::Grow: mremap from 31457280 to 32505856, result: Success
    W: DynamicMMap::Grow: mremap from 32505856 to 33554432, result: Success
    W: DynamicMMap::Grow: mremap from 33554432 to 34603008, result: Success
    W: DynamicMMap::Grow: mremap from 34603008 to 35651584, result: Success
    W: DynamicMMap::Grow: mremap from 35651584 to 36700160, result: Success
    W: DynamicMMap::Grow: mremap from 36700160 to 37748736, result: Success
    W: DynamicMMap::Grow: mremap from 37748736 to 38797312, result: Success
    W: DynamicMMap::Grow: mremap from 38797312 to 39845888, result: Success
    W: DynamicMMap::Grow: mremap from 39845888 to 40894464, result: Success
    W: DynamicMMap::Grow: mremap from 40894464 to 41943040, result: Success
    W: DynamicMMap::Grow: mremap from 41943040 to 42991616, result: Success
    W: DynamicMMap::Grow: mremap from 42991616 to 44040192, result: Success
    W: You may want to run apt-get update to correct these problems
    [imz@e801-1 ~]$ 

Ещё комментарии на тему этих изменений:
https://lists.altlinux.org/pipermail/devel/2019-October/208746.html
Comment 6 Anton Farygin 2019-10-24 17:49:55 MSK
Так эта проблема исправлена на e2k ?
Comment 7 Ivan Zakharyaschev 2019-10-24 18:03:29 MSK
(In reply to comment #6)
> Так эта проблема исправлена на e2k ?

Да, сейчас работает 0.5.15lorg2-alt71.1.1 (ветка alt у меня), но это не окончательный вариант кода ради исправления.
Comment 8 Anton Farygin 2019-10-24 18:16:29 MSK
на Debian/e2k не воспроизводится, т.к. там банально маленькие pkglist'ы, а у нас они сильно раздуты - объём репозитория заметно больше и есть set version в библиотеках.

А у МЦСТ два диска хеши чуть больше мегабайта, там просто до mremap может не дойти.
Comment 9 Ivan Zakharyaschev 2019-10-24 18:39:19 MSK
(In reply to comment #8)
> на Debian/e2k не воспроизводится, т.к. там банально маленькие pkglist'ы, а у
> нас они сильно раздуты - объём репозитория заметно больше и есть set version в
> библиотеках.
> 
> А у МЦСТ два диска хеши чуть больше мегабайта, там просто до mremap может не
> дойти.

Можно для тестирования искусственно занизить APT::Cache-Start , если у них есть аналогичный параметр.
Comment 10 Andrew Savchenko 2019-10-24 19:04:33 MSK
(In reply to comment #3)
> ## Озабоченность из-за alignment данных после перемещения
> 
> А также озабоченность вызывает (но я в этом не уверен, мало обдумывал)
> то, что все эти даныые могут оказаться после перемещения неудачно
> выравнены (aligned) в памяти, а некоторые платформы чувствительны к
> unaligned access к данным. (Платформы скорее не наши: старый arm,
> mips, ia64, но всё же нехорошо об этом не подумать; там это может быть
> ошибка или замедление в зависмости от режима работы.)

Всё же наши: для e2k это очень существенно. На лекциях компиляторщика МЦСТ этой весной особо подчёркивалось, что у e2k есть много эффективных механизмов повышения эффективности (в т.ч. асинхронная подкачка данных из L2 в регистры), но всё это работает только для выровненных данных.

Так что это задача, с одной стороны, второстепенная, т.к. главное — чтоб apt работал, пусть и медленно; а с другой стороны — данные очень желательно держать выровненными на e2k, даже ценой увеличенного расхода памяти (её там обычно достаточно).