From fae82e0de3043c9c75dd9f612306c34947bf9f8f Mon Sep 17 00:00:00 2001
From: Florian Schmaus <flow@cs.fau.de>
Date: Sat, 19 Feb 2022 15:43:57 +0100
Subject: [PATCH] [LinuxVersion] Use "Construct on first use" idiom and add
 mutex

Use the "Construct on first use" idom for UTS Release (aka Linux
version) initialization. The previous implementation was fragle and
could lead to segfaults like

    __s2=0x7fffffffe480 "5.16.10-arch1-1", __s1=<optimized out>)
    at /usr/include/c++/11.2.0/bits/char_traits.h:409
    __n=<optimized out>, __s=0x7fffffffe480 "5.16.10-arch1-1", __d=<optimized out>)
    at /usr/include/c++/11.2.0/bits/basic_string.h:359
    __n=<optimized out>, __s=0x7fffffffe480 "5.16.10-arch1-1", __d=<optimized out>)
    at /usr/include/c++/11.2.0/bits/basic_string.h:354
    __str=..., this=0x7ffff7fbd2a0 <emper::lib::LinuxVersion::globalVersion[abi:cxx11]>)
    at /usr/include/c++/11.2.0/bits/basic_string.h:739
    at ../emper/lib/LinuxVersion.cpp:46
    at /usr/include/c++/11.2.0/ext/new_allocator.h:79

if globalVersion's constructor was not yet called. This is the so
called "static initialization order fiasco" in C++.

While the mutex may not be strictly requires, as we do not call
LinuxVersion's non-argument constructor nor getUtsRelease()
concurrently, it can not hurt to have one.
---
 emper/lib/LinuxVersion.cpp | 32 ++++++++++++++++++++++----------
 emper/lib/LinuxVersion.hpp |  4 ++--
 2 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/emper/lib/LinuxVersion.cpp b/emper/lib/LinuxVersion.cpp
index 614407ce..af5ebf0c 100644
--- a/emper/lib/LinuxVersion.cpp
+++ b/emper/lib/LinuxVersion.cpp
@@ -1,11 +1,12 @@
 // SPDX-License-Identifier: LGPL-3.0-or-later
-// Copyright © 2021 Florian Fischer
+// Copyright © 2021-2022 Florian Fischer, Florian Schmaus
 #include "lib/LinuxVersion.hpp"
 
 #include <sys/utsname.h>
 
 #include <cerrno>
 #include <cstdlib>
+#include <mutex>
 
 #include "Common.hpp"
 
@@ -35,19 +36,30 @@ static auto checked_strtol(const std::string& s) -> long {
 
 namespace emper::lib {
 
-std::string LinuxVersion::globalVersion;
-
-LinuxVersion::LinuxVersion() {
-	if (globalVersion.empty()) {
-		struct utsname buf;
-		if (uname(&buf)) {
-			DIE_MSG_ERRNO("Failed to invoke uname()");
+const std::string& LinuxVersion::getUtsRelease() {
+	// Use "construct (members) on first use idiom" to prevent the "static initialization order
+	// fiasco/problem".
+	static std::string utsRelease;
+	// This mutex will become necessary once we call getUtsRelease(),
+	// and by implication LinuxVersion's constructor, concurrently. So
+	// it can not hurt to have it right now.
+	static std::mutex utsReleaseMutex;
+
+	{
+		const std::lock_guard<std::mutex> lock(utsReleaseMutex);
+		if (utsRelease.empty()) {
+			struct utsname buf;
+			if (uname(&buf)) {
+				DIE_MSG_ERRNO("Failed to invoke uname()");
+			}
+			utsRelease = std::string(buf.release);
 		}
-		globalVersion = std::string(buf.release);
 	}
-	version = globalVersion;
+	return utsRelease;
 }
 
+LinuxVersion::LinuxVersion() { version = getUtsRelease(); }
+
 // Returns 1 if s is smaller, -1 if this is smaller, 0 if equal
 auto LinuxVersion::compare(const std::string& s) -> int {
 	size_t dot_pos, dot_pos2;
diff --git a/emper/lib/LinuxVersion.hpp b/emper/lib/LinuxVersion.hpp
index 2d3b5713..8af11701 100644
--- a/emper/lib/LinuxVersion.hpp
+++ b/emper/lib/LinuxVersion.hpp
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: LGPL-3.0-or-later
-// Copyright © 2021 Florian Fischer
+// Copyright © 2021-2022 Florian Fischer, Florian Schmaus
 #pragma once
 
 #include <string>
@@ -9,7 +9,7 @@ class TestLinuxVersion;
 namespace emper::lib {
 class LinuxVersion {
 	friend class ::TestLinuxVersion;
-	static std::string globalVersion;
+	static const std::string& getUtsRelease();
 	std::string version;
 
 	auto compare(const std::string& s) -> int;
-- 
GitLab