• Re: "C++ Must Become Safer" by Andrew Lilley Brinker

    From David Brown@21:1/5 to Lynn McGuire on Fri Jul 19 13:40:04 2024
    On 18/07/2024 23:41, Lynn McGuire wrote:
    "C++ Must Become Safer" by Andrew Lilley Brinker
       https://www.alilleybrinker.com/blog/cpp-must-become-safer/

    "Not everything will be rewritten in Rust, so C++ must become safer, and
    we should all care about C++ becoming safer."

    "It has become increasingly apparent that not only do many programmers
    see the benefits of memory safety, but policymakers do as well. The
    concept of “memory safety” has gone from a technical term used in discussions by the builders and users of programming languages to a term known to Consumer Reports and the White House. The key contention is
    that software weaknesses and vulnerabilities have important societal
    impacts — software systems play critical roles in nearly every part of
    our lives and society — and so making software more secure matters, and improving memory safety has been identified as a high-leverage means to
    do so."

    Not gonna happen since to do so would remove the purpose of C and C++.

    Lynn

    The problem with a lot of "memory safe language" ramblings is that so
    many people seem to imagine that "memory safety" is a binary issue.
    They divide languages into two groups - "memory safe" languages and
    "memory unsafe" languages and think that all code written in the former category is automatically "safe", and all code written in the later is inherently "unsafe".

    This is, of course, total drivel.

    It is not at all hard to have resource leaks in garbage collected
    languages. You can happily have out of bounds accesses that crash the
    program. Equally, you can perfectly well write safe code in C and C++.

    The "safe" languages can make it harder to write certain kinds of
    mistakes in the code, and can reduce the consequences of errors. They
    can automate some things, reducing the manual work and error risk
    involved. And that's great. There's a lot that gets written in
    low-level languages such as C when it would be more productive and lower
    risk to use a higher level language.

    And with modern C++, you can also automate lots of things and write
    clearly memory-safe code, just as well as many managed languages. The difference is that with C++ you can /also/ write unsafe code with all
    the risks available to more manual low-level programming. Resource
    management with smart pointers and standard containers can be as "memory
    safe" as code written in Python - but C++ also lets you use manual
    malloc() and C-style arrays and pointers, with the risks these entail.
    (Of course you can write correct memory-safe code in that style too.)


    I believe that the people who re-write ancient C code in Rust, producing
    code with fewer bugs, could have had just as much success if they had re-written the old C code in new C, using modern C standards, modern C
    tools, and modern C development techniques. It's not the language
    change that makes the difference - it's throwing out accumulated cruft, avoiding old, riskier techniques, and writing code with an emphasis on
    safety and correctness instead of trying to get efficient results from
    limited tools.


    That said, I think it will be good to see more safety-related features
    added to C++, such as contracts and borrow checking. And more static
    analysis to make it harder to write risky code is always a nice thing.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paavo Helde@21:1/5 to Lynn McGuire on Tue Jul 23 11:48:47 2024
    On 23.07.2024 04:38, Lynn McGuire wrote:
    On 7/19/2024 6:40 AM, David Brown wrote:
    That said, I think it will be good to see more safety-related features
    added to C++, such as contracts and borrow checking.  And more static
    analysis to make it harder to write risky code is always a nice thing.

    What is borrow checking ?

    Looks like built-in and default-used std::unique_ptr + std::move(), so
    the Rust compiler can e.g. issue compile-time errors when a moved-away
    variable is used.


    For C++ the same is done by e.g. MSVC++ analyzer and other static
    analysis tools:

    #include <vector>
    void foo(std::vector<int>&& v) {}

    int main() {
    std::vector<int> vv{ 1,2,3,4 };
    foo(std::move(vv));
    return vv.empty();
    }
    main.cpp(7): warning C26800: Use of a moved from object: ''vv''
    (lifetime.1).


    Plus in Rust it allegedly also helps with multithread locking, though I
    was not able to figure it out in 10 minutes of googling.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Lynn McGuire on Tue Jul 23 22:59:49 2024
    Lynn McGuire <lynnmcguire5@gmail.com> writes:
    On 7/23/2024 3:48 AM, Paavo Helde wrote:
    On 23.07.2024 04:38, Lynn McGuire wrote:
    On 7/19/2024 6:40 AM, David Brown wrote:
    That said, I think it will be good to see more safety-related
    features added to C++, such as contracts and borrow checking.  And
    more static analysis to make it harder to write risky code is always
    a nice thing.

    What is borrow checking ?

    Looks like built-in and default-used std::unique_ptr + std::move(), so
    the Rust compiler can e.g. issue compile-time errors when a moved-away
    variable is used.


    For C++ the same is done by e.g. MSVC++ analyzer and other static
    analysis tools:

    #include <vector>
    void foo(std::vector<int>&& v) {}

    int main() {
        std::vector<int> vv{ 1,2,3,4 };
        foo(std::move(vv));
        return vv.empty();
    }
    main.cpp(7): warning C26800: Use of a moved from object: ''vv''
    (lifetime.1).


    Plus in Rust it allegedly also helps with multithread locking, though I
    was not able to figure it out in 10 minutes of googling.

    Ah, my multithreading has not been good so far. I have three threads in
    my main app and none of the variables involved cross the thread boundaries.

    Whereas:

    (gdb) thread apply all bt

    Thread 89 (Thread 0x7fffc2cca700 (LWP 35934)):
    #0 0x00007ffff775ead3 in futex_wait_cancelable (private=<optimized out>, expected=0x0, futex_word=0x7fffc2ccb0e8) at ../sysdeps/unix/sysv/linux/futex-internal.h:88
    #1 __pthread_cond_wait_common (abstime=0x0, mutex=0x7fffc2ccb0f0, cond=0x7fffc2ccb0c0) at pthread_cond_wait.c:502
    #2 __pthread_cond_wait (cond=cond@entry=0x7fffc2ccb0c0, mutex=mutex@entry=0x7fffc2ccb0f0) at pthread_cond_wait.c:655
    #3 0x0000555555893213 in c_condition::wait (lockp=0x7fffc2ccb0f0, this=0x7fffc2ccb0c0)

    All the threads share access to a set of common resources. A
    subset of the threads run at 100% utilization, the remaining threads
    are waiting for some condition to become true, whereafter they
    do some work and return to the waiting condition when the condition
    becomes not true.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paavo Helde@21:1/5 to Bonita Montero on Wed Jul 24 12:12:08 2024
    On 24.07.2024 10:28, Bonita Montero wrote:
    Am 23.07.2024 um 10:48 schrieb Paavo Helde:
    For C++ the same is done by e.g. MSVC++ analyzer and other static
    analysis tools:

    #include <vector>
    void foo(std::vector<int>&& v) {}

    int main() {
         std::vector<int> vv{ 1,2,3,4 };
         foo(std::move(vv));
         return vv.empty();
    }
    main.cpp(7): warning C26800: Use of a moved from object: ''vv''
    (lifetime.1).


    Plus in Rust it allegedly also helps with multithread locking,
    though I was not able to figure it out in 10 minutes of googling.

    C++ says that moved from objects are left in an indefinite state,
    but for vectors this actually doesn't happen. With strings this
    might happen due to the short string optimization so that there
    might be a copy instead of a move.

    This is not what the standard says. There is nothing indefinite about
    the state of the object after move, this is not some quantum theory.

    What the standard actually says (about C++ standard library types):
    "Unless otherwise specified, such moved-from objects shall be placed in
    a valid but unspecified state".

    This just means the standard does not prescribe to the implementations
    in which state the objects will be left, and therefore a programmer
    should not rely on expecting some certain state. Hence the static
    analyzer warnings and Rust errors when accessing the moved-away state.

    Unfortunately, the C++ standard has messed itself up again by sometimes requiring a specific state after move. For example,
    std::map::try_emplace: "Unlike insert or emplace, these functions do not
    move from rvalue arguments if the insertion does not happen."

    That's pretty unfortunate. I can see that such interfaces might be
    useful for squeezing out the max possible performance, but IMO they
    should be defined in terms of normal lvalue references, instead of
    rvalue references.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Paavo Helde on Thu Jul 25 12:10:33 2024
    On 23/07/2024 10:48, Paavo Helde wrote:
    On 23.07.2024 04:38, Lynn McGuire wrote:
    On 7/19/2024 6:40 AM, David Brown wrote:
    That said, I think it will be good to see more safety-related
    features added to C++, such as contracts and borrow checking.  And
    more static analysis to make it harder to write risky code is always
    a nice thing.

    What is borrow checking ?

    Looks like built-in and default-used std::unique_ptr + std::move(), so
    the Rust compiler can e.g. issue compile-time errors when a moved-away variable is used.


    For C++ the same is done by e.g. MSVC++ analyzer and other static
    analysis tools:

    #include <vector>
    void foo(std::vector<int>&& v) {}

    int main() {
        std::vector<int> vv{ 1,2,3,4 };
        foo(std::move(vv));
        return vv.empty();
    }
    main.cpp(7): warning C26800: Use of a moved from object: ''vv''
    (lifetime.1).


    Plus in Rust it allegedly also helps with multithread locking, though I
    was not able to figure it out in 10 minutes of googling.



    Yes, that's how I understand it. (I know very little of the details of
    Rust, so I am happy to be corrected if I make any mistakes here.) Rust
    has a "borrow checker" program that runs in addition to the compiler, to
    track ownership of objects and resources and be sure that the ownership
    rules are followed. C++ compilers can do some checking, as you show
    above, just as Rust compilers can. But to do it properly requires
    inter-module analysis and that's where the external borrow checker
    program comes in. (AFAIUI, there is a Rust frontend for gcc, but they
    have not yet got a companion borrow checker.)

    I think it should be possible to make a similar borrow checker program
    for C++ that tracks usage and ensures you don't use objects incorrectly
    after moves, even as they are passed across modules. There would be a
    key difference, however - Rust is designed to make it as hard as
    possible to break these kinds of rules, while C++ has no such
    restrictions. Borrow-safe C++ code would require you to use the
    specific borrow-safe types (i.e., no raw pointers and the like).

    There is a language XC made by XMOS for use with their microcontrollers.
    This is designed for hard real-time software with hardware-assisted
    RTOS and message passing (inspired by CSP). XC is based on C, but with
    some restrictions and limitations, and several additions and
    enhancements. One of the features is that the XC tools know which
    thread "owns" a given piece of data, and stops you from accessing it incorrectly with from a different thread. (It's arguably a bit too
    strict - sometimes you know things are safe, but the tools block you
    from the accesses you want.) There could be ways to get similar
    checking for C++ (again, it would no doubt be restricted to a specific
    subset of C++).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Vir Campestris@21:1/5 to David Brown on Thu Jul 25 12:58:38 2024
    On 19/07/2024 12:40, David Brown wrote:
    <snip>
    And with modern C++, you can also automate lots of things and write
    clearly memory-safe code, just as well as many managed languages.  The difference is that with C++ you can /also/ write unsafe code with all
    the risks available to more manual low-level programming.  Resource management with smart pointers and standard containers can be as "memory safe" as code written in Python - but C++ also lets you use manual
    malloc() and C-style arrays and pointers, with the risks these entail.
    (Of course you can write correct memory-safe code in that style too.)
    </snip>

    The very first thing I was asked to do in my last job was to look at
    system crashes. I worked out that one of the common ones was a resource management issue - it was allocating objects and freeing them
    dynamically, losing track of which objects had been freed, and accessing
    them after they had been deleted. It was all done with raw pointers.

    I replaced them all with shared_ptr and weak_ptr.

    System reliability went up. Noticeably. But in one place I used a
    shared_ptr that should have been weak_ptr - so under certain
    circumstances the code "leaked" memory, not unloading objects it had
    finished with.

    I replaced a classic C problem with one you could quite easily get in
    Java or C#, or I assume Rust.

    There are no magic bullets.

    Andy

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From jseigh@21:1/5 to Lynn McGuire on Mon Jul 29 11:06:03 2024
    On 7/18/24 17:41, Lynn McGuire wrote:
    "C++ Must Become Safer" by Andrew Lilley Brinker
       https://www.alilleybrinker.com/blog/cpp-must-become-safer/

    "Not everything will be rewritten in Rust, so C++ must become safer, and
    we should all care about C++ becoming safer."

    "It has become increasingly apparent that not only do many programmers
    see the benefits of memory safety, but policymakers do as well. The
    concept of “memory safety” has gone from a technical term used in discussions by the builders and users of programming languages to a term known to Consumer Reports and the White House. The key contention is
    that software weaknesses and vulnerabilities have important societal
    impacts — software systems play critical roles in nearly every part of
    our lives and society — and so making software more secure matters, and improving memory safety has been identified as a high-leverage means to
    do so."

    Not gonna happen since to do so would remove the purpose of C and C++.

    Lynn

    I did look at Rust (mostly askance :)). I decided to take a pass on it
    because of the allegedly long time to take to learn it and it didn't
    really address concurrency (at least the stuff I mess with). Also they
    keep making unsubstantiated claims that Rust is more performant than
    Java, also a "memory safe" language. Apparently they are unaware of
    Java's JIT compilation and how good concurrent garbage collection can
    be. This from where reference counting, one of the least performant
    ways of managing shared memory, is insanely popular.

    Its "memory safety" is type safety I think, if you ignore concurrency.
    It's solved the aliasing problem somewhat. I do like its send trait,
    "you've just send this heap based object to another thread so don't mess
    with it from now on". This is mostly just impressions I got from
    reading the Rust users form which has a lot of help related stuff so
    there might be some sample bias there.

    Its handling of mutability of referenced objects is a little weird.

    Access to locked object is done by "dereferencing" the mutex. So no
    using mutexes on groups of things. You would have to create a type to
    hold that group and use the mutex on that.

    Joe Seigh

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)