HN user

snnn

155 karma
Posts0
Comments105
View on HN
No posts found.

Most people in Tibet only speak Tibetan. They also need to use smart phones. They type texts on their phones to communicate with their friends. They simply cannot use Latin alphabet for doing that.

Kernel developers need to consider backwards compatibility. You won't want to see some users lose their data because they upgraded the kernel. Therefore it is very hard to "force" something.

Because that's how "dirname(3)" is implemented in glibc, except it searches '/' instead of '\'. Here all character encodings share the same code.

But the reality is: most glibc functions like `dirname` could not handle non UTF-8 encodings, because some encodings (like GBK) have overlaps with ASCII, which means when you search an ASCII char(like '\') in a char array, you may accidentally hit a half of a non-English character. Therefore, people in Asia usually do not use the non UTF-8 locales.

Man, if English is the only human language in this world, who would need UTF-8? The other encodings exist because they are more efficient for the other languages. Especially, for the Chinese, Japanese, and Korean languages. UTF-8 takes 50% more space than the alternatives. To bad modern Linux systems only support UTF-8 locales.

Maybe we should consider moving more and more system process to webassembly. wasmtime has a nice sandbox. Surely it will decrease the performance, but performance is not always that important. For example, on my dev machine even if SSHD or apache's performance dropped 3x because of that, I wouldn't mind. If I really care, spend more money to get a more powerful CPU.

ClamAV also has a lot of findings when scanning some open source project's source code. For example, LLVM project's test data. Because some of the test data are meant to check if a known security bug is fixed, from a antivirus software perspective these data files can be seen as exploits. ClamAV is commonly used. Or, I would suggest adding it to every CI build pipeline. Most time it wouldn't have any finding, but it is better than nothing. I would like to offer free help if an open source project has the need to harden their build pipelines and their release process.

to try to overwrite symbols in other modules, to add LD audit hooks on startup, to try to resolve things manually by walking ELF structures

I want to name one thing: when Windows failed to load a DLL because a dependency was missing, it doesn't tell you what was missed. To get the information, you have to interact with the DLL loader with low level Windows APIs. In some circumstances Linux apps may also have the need. Like for printing a user friendly error message or recovery from a non-fatal error. For example, the patchelf tool that is used for building portable python packages.

No one wants a Linux antivirus

It is not true. Actually these software are very popular in enterprise settings.

Actually, the new architectures are a big source of concerns. As a maintainer of a large open source project, I often received pull requests for CPU architectures that I never had a chance to touch. Therefore I cannot build the code, cannot run the tests, and do not understand most of the code. C/C++ themselves are portable, but libs like xz needs to beat the other competitors on performance, which means you may need to use model specific SIMD instructions, query CPU cache size and topology, work at very low level. These code are not portable. When people add these code, they often need to add some tests, or disable some existing tests conditionally, or tweak the build scripts. So they are all risks.

No matter how smart you are, you cannot forecast the future. Now many CPUs have a heterogeneous configuration, which means they have big cores and little cores. But do all the cores have the same capabilities? Is possible that a CPU instruction only available on some of the CPU cores? What does it mean for a multithreaded application? Would it be possible that 64-bit CPUs may drop the support for 32-bit at hardware level? Tens years ago you cannot predict what's going to happen today.

Windows has a large compatibility layer, which allows you running old code on the latest hardware and latest Windows. It needs quite a lot efforts. Many applications would crash without the compatibility patches.

I mostly agree with you, but I think your argument is wrong. Last month I found a tiny bug in Unix's fgrep program(the bug has no risk). The program implements Aho Corasick algorithm, which hasn't changed much over decades. However, at least when the code was released to 4.4BSD, the bug still existed. It is not much a concern as nowadays most fgrep progroms are just an alias of grep. They do not use the old Unix code anymore. The old Unix code, and much part of FreeBSD, really couldn't meet today's security standard.For example, many text processing programs are vulnerable to DoS attacks when processing well-crafted input strings. I agree with you that in many cases we really don't need to touch the old code. However, it is not just because the algorithm didn't change.

Some USB keys have a LCD screen on it to prevent that. You can comprise the computer that the key was inserted to, but you cannot comprise the key. If you see the things messages shows up on your computer screen differs from the messages on the key, you reject the auth request.

I don't think it would help much. I work on machine learning frameworks. A lot of them(and math libraries) rely on just in time compilation. None of us has the time or expertise to inspect JIT-ed assembly code. Not even mentioning that much of the code deliberately read/write out of bound, which is not an issue if you always add some extra bytes at the end of each buffer, which could make most memory sanitizer tools useless. When you run their unit tests, you run the JIT code, then a lot of things could happen. Maybe we should ask all packaging systems splitting their build into compile and test two stages, to ensure that a testing code would not impact the binaries that are going to be published. I would rather to read and analysis the generated code instead of the code that generates it.

The mechanisms for Windows DLLs have been changed a lot(like how thread local vars are handled). Besides, this book could not cover C++11's magic statics, or Windows' ARM64X format, or Apple's universal2, because these things are very new. Windows now has the apiset concept, which is very unique. Upon it there are direct forwarding and reverse forwarders.

I think for C/C++ programmers it is more practical to know that: 1. The construction/destruction order for global vars in DLLs(shared libs) are very different between Linux and Windows. It means the same code might work fine on one platform but no the other one. It imposes challenges on writing portable code. 2. On Linux it is hard to get a shared lib cleanly unloaded, and it might affect how global vars are destructed, and might cause unexpected crashes at exit. 3. Since Windows has a DLL loader lock, there are a lot of things you cannot do in C++ classes constructors/destructors if the classes could be used to define a global variable. For example, no thread synchronization is allowed. 4. It is difficult to cleanup a thread local variable if the variable lies in a DLL and the variable's destructor depends on another global object. 5. When one static lib depends on another, a linker wouldn't use this information to sort the initialization order of global vars. It means it could be the case that A.lib depends on B.lib but A.lib get initialized first. The best way to avoid this problem is using dynamic linking.

For Windows related topics I highly recommend the "Windows Internals" book.

As a software engineer who pays more attention on security than average, I often feel my work didn't get recognized. For example, if I successfully prevented a supply chain attack, nobody would say thanks to me for a thing that didn't happen, even when they see a competitor product gets attacked. Similarly, most C/C++ programmers do not really care about integer overflow. But I know we are no longer in the world that computer viruses are everywhere(though ransomware are still common). The, who made it better? The people who are not satisfied with stuff that "just works".

If you only test buffer overruns, VC++ static analyzer + SAL2 can do an excellent job on this. Basically if you annotate every pointer with a length, the compiler can tell you if a pointer arithmetic is safe or not.

Actually these GNU tools are relatively simple, compared to the code we usually write as a C/C++ software engineer at daily work. For example, if you have a function that takes just one single protobuf object, Klee cannot help you. Because the input space is too large. Klee can only operate at unit test level, with special crafted code. It did a good job on GNU textutils because the inputs of the each tool are relatively independent, and most inputs are just boolean flags that are either true or false. Also, please be aware that klee cannot provide any kind of assurance. Normally it cannot give you a proof saying your code is 100% safe, because most code are too complex to reach that. I'm saying while it usually tries to find all the execution paths of your code and execute them symbolically, usually it is not possible to finish executing all the paths. Though Klee can support C++, you will find fuzzing C programs is much easier than C++, because C++ data structures are way more complicated. Like, a C-style string vs a C++ std::string. A C array vs a std::vector. So, in order to get a broader usage of Klee, we need to rewrite our code in a simpler way.

Ideally the builds for external PRs should not gain any access to any secret. But it is not practical. For example, you may want to use docker containers. Then you will need to download docker images from somewhere. For example, downloading ubuntu:20.04 from docker hub. That one requires a token, otherwise your request may get throttled. Even accessing most Github services requires a token. I agree with you that these things require a lot of care. In reality most dev teams are not willing to put enough time on securing their build system. That's why supply chain attacks are so common today.

Compare to codesign, vulnerability management is more concerning. Ubuntu users should know that security patches for ImageMagick are not free! If you do not believe that, read this https://ubuntu.com/security/notices/USN-6393-1. The security patch is only provided through Ubuntu's Expanded Security Maintenance (ESM) plan, which means you must pay for it. So, seriously, consider having you own build. Then there is no need to worry about codesign too.

Bun v0.8 3 years ago

On Windows you may consider using higher level IO routines. For example, for HTTP requests you can use WinHTTP which is super fast and scalable. For other IOs you can use Windows Thread Pool API(https://learn.microsoft.com/en-us/windows/win32/procthread/t...) so that you do not need to manually manage threads or register/unregister IO handlers/callbacks. gRPC uses that. Though Windows IOs internally are all async, actually it makes using sync I/O easier and you do not need to say it is a super bad idea. Windows has IOCP. If the machine has n logical CPUs, you may create a thread pool with 2*n threads. And, by default the operating system will not make more than n threads active at the same time. When one of the threads is doing blocking IO and entered IO wait state, the OS will wake-up another thread and let it go. This is why the number of threads in the thread pool needs be larger than than the number of CPUs. This design doesn't lead to an optimal solution, however, practically it works very well. In this setting you still have the flexibility to use async IOs, but it is not a sin to use sync IO in a blocking manner in a thread pool.

Disclaimer: I work at Microsoft and ship code to Windows, but the above are just my personal opinions.