I don't have experience with avionics, but I do have some experience with medical and the principles are similar.
In my not very humble opinion, the main challenges are paperwork, paperwork and more paperwork and having to live with technology that is either 30 years old or total usability horror, because it is not objectively safer, but happens to have the paperwork done for it.
For safety critical systems, of any kind, not just software, it is required that risk analysis is done and the system must be designed and implemented so that qualified estimate of mean time between critical failures (= failures that result in an accident) is lower than some value. For systems that can kill multiple people the requirement is usually 10⁹ hours.
That estimate involves all kinds of failures, both software and hardware, and across all parts of the system. The components (both software and hardware) are tested to get some basic reliability for them, and then checks and backups have to be provided so that any particular type of fault, which is almost always more likely than once per 10⁹ hours, won't result in an accident, and the combined probability of enough faults to cause one at once is below the desired threshold.
Now for software that means that it has to be comprehensively tested, which is fine. But it is also required that the tools involved in building it are “validated”. The stated purpose is that you know what is the risk of fault in the tool creating an undetected fault in the product, which is needed for the overall risk analysis. However:
The way I've seen it done in practice usually missed the point completely. Some requirements were created and then the tool tested to show it satisfies them. This was a lot of work, but the requirements defining what the tool is supposed to do—e.g. for a compile the specification of the language—didn't really tell anything about either risk of the tool introducing a bug—e.g. compiler producing functionally different code in debug and release mode—nor the risk of it becoming critical—if you test what you fly and fly what you test, you don't create any different builds that could have a difference.
Due to the amount of work involved it means you are often stuck with a special compiler that only supports old standard (like C89), has a lot of known limitations and comes with its own terrible, ugly IDE, only because the vendor did the validation for it while the general tools like gcc
or clang
don't have it. Now take into account that the validation only really makes sense in context of the specific project, so all that paperwork may not really improve the reliability of the product, just serves as an important liability shield for the company.
Fortunately the code itself tends to be fairly simple. Anything that should be real-time, i.e. have guaranteed response time, which is case of most avionics, can't be too complex, because you couldn't verify it does not have any slow worst cases.
So specifically for software engineering I'd say the current challenge is that the surrounding processes provide diminishing returns on reliability while ever increasing the barrier for innovation.