Tanımlanmamış davranış içeren kaynak kodun derleyiciyi çökertmesi yasal mı?


85

Diyelim ki tanımlanmamış bir davranışı çağıran kötü yazılmış C ++ kaynak kodunu derlemeye gittiğimi ve bu nedenle (dedikleri gibi) "her şey olabilir" diyelim.

C ++ dil spesifikasyonunun bir "uyumlu" derleyicide kabul edilebilir gördüğü bakış açısından, bu senaryoda "herhangi bir şey" derleyicinin çökmesini (veya parolalarımı çalmasını veya derleme zamanında yanlış davranma veya hata yapma) içerir veya tanımsız davranışın kapsamı, özellikle ortaya çıkan yürütülebilir dosya çalıştığında ne olabileceğiyle sınırlıdır?


22
"UB UB'dir. Onunla yaşayın" ... Beklemek yok. "Lütfen bir MCVE gönderin." ... Hayır bekle. Uygunsuz bir şekilde tetiklediği tüm refleksler için soruyu seviyorum. :-)
Yunnosch

14
Gerçekten bir sınırlama yok, bu yüzden UB'nin burun iblislerini çağırabileceği söyleniyor .
Bir programcı dostum

15
UB, yazarın SO'ya bir soru göndermesini sağlayabilir . : P
Tanveer Badar

46
C ++ standardının ne söylediğine bakılmaksızın, bir derleyici yazar olsaydım, bunu kesinlikle derleyicimde bir hata olarak görürdüm. Yani bunu görüyorsanız, bir kusur raporu sunun.
john

9
@LeifWillerts Bu 80'lerde geri döndü. Tam yapıyı hatırlamıyorum, ancak kıvrımlı bir değişken türü kullanmaya bağlı olduğunu düşünüyorum. Bir yedek taktıktan sonra "ne düşünüyordum - işler o şekilde yürümüyor" anı yaşadım. Derleyiciyi yapıyı reddettiği için, sadece makineyi yeniden başlattığı için suçlamadım. Bugün herhangi birinin bu derleyiciyle karşılaşacağından şüpheliyim. 68000 mikroişlemciyi hedefleyen HP 64000 için HP C çapraz derleyiciydi.
Avi Berger

Yanıtlar:


71

Tanımlanmamış davranışın normatif tanımı aşağıdaki gibidir:

[defns.undefined]

Bu standardın herhangi bir gereklilik getirmediği davranış

[Not: Bu Uluslararası Standart herhangi bir açık davranış tanımını atladığında veya bir program hatalı bir yapı veya hatalı veri kullandığında, tanımlanmamış davranış beklenebilir. İzin verilen tanımlanmamış davranış, durumu tamamen öngörülemeyen sonuçlarla göz ardı etmekten, çeviri veya program yürütme sırasında çevrenin özelliği olan belgelenmiş bir şekilde davranmaya (bir tanılama mesajı vererek veya yayınlamadan), bir çeviriyi veya yürütmeyi sonlandırmaya (yayınlama ile) bir teşhis mesajı). Birçok hatalı program yapısı tanımlanmamış davranışlara yol açmaz; teşhis edilmeleri gerekir. Sabit bir ifadenin değerlendirilmesi hiçbir zaman açıkça tanımsız olarak belirtilen davranışı göstermez. - son not]

Notun kendisi normatif olmasa da, sergilediği bilinen bir dizi davranış uygulamasını açıklamaktadır. Bu nedenle, derleyicinin çökmesi (çeviri aniden sona eriyor), bu nota göre meşru. Ama gerçekte, normatif metnin dediği gibi, standart ne uygulama ne de çeviri için herhangi bir sınır koymuyor. Bir uygulama parolalarınızı çalarsa, bu standartta belirtilen herhangi bir sözleşmenin ihlali değildir.


43
Yapabilecekleriniz, söyledi aslında bir derleyici sonra çeşitli güvenlik insanlar olur, herhangi bir korumalı alan olmadan, derleme sırasında rasgele kod yürütmesine almak çok bunu bilmek ilgi. Aynısı derleyiciyi segfaulting için de geçerlidir.
Kevin

67
Kevin'in dediği için aynen. Önceki bir kariyerde bir C / C ++ / etc derleyici mühendisi olarak, bizim pozisyonumuz, tanımlanmamış davranışların programınızı çökertebileceğiydi, çıktı verilerinizi bozabilir, evinizi ateşe verebilirdi . Ancak, girdi ne olursa olsun derleyici asla çökmemelidir. (Yardımcı hata mesajları vermeyebilir, ancak CTHULHU TEKERLEĞİ ÇEKİN ve segfaulting diye bağırmak yerine bir tür teşhis ve çıkış üretmelidir.)
Ti Strga

8
@TiStrga Eminim Cthulhu harika bir F1 pilotu olacaktı.
zeta-band

35
"Bir uygulama şifrelerinizi çalarsa, bu standartta belirtilen herhangi bir sözleşmenin ihlali değildir." Kodda UB olup olmadığına bakılmaksızın bu doğru, değil mi? Standart yalnızca derlenen programın ne yapması gerektiğini belirler - kodu doğru bir şekilde derleyen ancak işlem sırasında parolalarınızı çalan bir derleyici, standarda uymayacaktır.
Carmeister

8
@Carmeister, oooh, bu iyi bir nokta, bu "UB derleyiciye bir nükleer savaş başlatma izni verir" argümanları çıktığında insanlara bunu hatırlatacağımdan emin olacağım. Tekrar.
ilkkachu

8

NULL-deref veya sıfıra bölme gibi genellikle endişelendiğimiz çoğu UB türü çalışma zamanı UB'dir. Çalışma zamanı UB neden olacak bir işlev Derleme idam eğer derleyici çökmesine neden olmamalıdır. Belki sürece bu işlevi (ve görev ekseninde yol) kesinlikle kanıtlayabilirim olacak program tarafından yürütülecek.

(İkinci düşünceler: belki derleme zamanında şablon / constexpr gerekli değerlendirmeyi düşünmemiştim. Muhtemelen bu sırada UB, sonuçta ortaya çıkan işlev asla çağrılmasa bile çeviri sırasında keyfi tuhaflığa neden olabilir.)

Çeviri sırasında davranıyor ISO C ++ alıntı parçası Masalcı cevabı @ de ISO C standardında kullanılan dilin benzer. C şablonları içermez veyaconstexpr , derleme zamanında zorunlu değerlendirmeyi .

Ancak eğlenceli gerçek : ISO C, bir notta, çevirinin sonlandırılması durumunda bir teşhis mesajı ile birlikte olması gerektiğini söylüyor. Veya "çeviri sırasında ... belgelenmiş bir şekilde davranmak". "Durumu tamamen görmezden gelmenin" çeviriyi durdurmayı içerdiğini düşünmüyorum.


Çeviri zamanı UB'yi öğrenmeden önce yazılmış eski cevap. Runtime-UB için doğrudur ve bu nedenle potansiyel olarak hala yararlıdır.


UB diye bir şey yoktur olur derleme sırasında. Bu olabilir görünür yürütmenin belli bir yol boyunca derleyici, ancak C ++ açısından bu henüz oldu yürütme varıncaya kadar bir fonksiyonu sayesinde icra yolu olduğu.

Bir programdaki derlemeyi bile imkansız kılan kusurlar UB değildir, sözdizimi hatalarıdır. Böyle bir program C ++ terminolojisine göre "iyi biçimlendirilmemiş" değildir (eğer standartlarım doğru ise). Bir program iyi biçimlendirilebilir ancak UB içerebilir. Tanımsız Davranış ve Kötü Biçim arasındaki fark, teşhis mesajı gerekmez

Bir şeyi yanlış anlamıyorsam, ISO C ++ bu programın doğru bir şekilde derlenmesini ve yürütülmesini gerektirir , çünkü yürütme asla sıfıra bölmeye ulaşmaz. (Pratikte ( Godbolt ), iyi derleyiciler sadece çalışan yürütülebilir dosyalar yaparlar. Gcc / clang bu konuda uyarır x / 0, ancak bu konuda uyarır , ancak optimize ederken bile. Ancak yine de, düşük ISO C ++ 'nın uygulama kalitesinin ne kadar düşük olmasına izin verdiğini anlatmaya çalışıyoruz . / clang, programı doğru yazdığımı doğrulamak dışında pek kullanışlı bir test değildir.)

int cause_UB() {
    int x=0;
    return 1 / x;      // UB if ever reached.
 // Note I'm avoiding  x/0  in case that counts as translation time UB.
 // UB still obvious when optimizing across statements, though.
}

int main(){
    if (0)
        cause_UB();
}

Bunun için bir kullanım durumu, C ön işlemcisini veya constexprdeğişkenleri ve bu değişkenler üzerinde dallanmayı içerebilir; bu, bu sabitlerin seçimleri için hiçbir zaman ulaşılamayan bazı yollarda anlamsızlığa yol açar.

Derleme zamanında görünür UB'ye neden olan yürütme yollarının hiçbir zaman alınmayacağı varsayılabilir, örneğin, x86 için bir derleyici ud2, tanımı olarak bir (geçersiz talimat istisnasına neden olabilir) yayabilir cause_UB(). Veya bir işlev içinde, bir tarafın kanıtlanabilir UB'ye if()yol açması durumunda, dal kaldırılabilir.

Ancak derleyicinin diğer her şeyi mantıklı ve doğru bir şekilde derlemesi gerekir . Tüm yollar yok karşılaşma (veya karşılaşma kanıtlanamayan) UB hala yürütür olarak eğer C ++ soyut makine çalışan olduğu asm derlenmelidir.


Koşulsuz derleme zamanında görünür UB girişinin mainbu kuralın bir istisnası olduğunu iddia edebilirsiniz . Veya başka türlü derleme zamanı kanıtlanabilir olan yürütmemain aslında garantili UB'ye ulaşır.

Ben hala yasal derleyici davranışları bir el bombası olduğunu patlar üreten içerir iddia ediyorum eğer koşmak. Ya da daha makul bir şekilde, bunun tanımı maintek bir yasadışı talimattan ibarettir. Programı hiç çalıştırmazsanız, henüz UB'nin olmadığını iddia ediyorum . Derleyicinin kendisinin patlamasına izin verilmez, IMO.


Dalların içinde olası veya kanıtlanabilir UB içeren işlevler

Herhangi bir yürütme yolu boyunca UB, önceki tüm kodu "kirletmek" için zamanda geriye doğru gider. Ancak pratikte derleyiciler bu kuraldan yalnızca yürütme yollarının derleme zamanında görünür UB'ye yol açtığını gerçekten kanıtlayabildiklerinde yararlanabilirler. Örneğin

int minefield(int x) {
    if (x == 3) {
        *(char*)nullptr = x/0;
    }

    return x * 5;
}

Derleyici, INT_MIN ve INT_MAX'ta işaretli taşma UB'ye neden xolan noktalara kadar, 3 dışındaki tüm kullanıcılar için çalışan bir asm x * 5yapmalıdır. Bu işlev asla çağrılmazsa x==3, program elbette UB içermez ve yazıldığı gibi çalışmalıdır.

if(x == 3) __builtin_unreachable();Derleyiciye bunun xkesinlikle 3 olmadığını söylemek için GNU C'de yazmış olabiliriz .

Pratikte normal programlarda her yerde "mayın tarlası" kodu vardır. örneğin bir tamsayıya göre herhangi bir bölme, derleyiciye sıfır olmadığını vaat eder. Herhangi bir işaretçi deref, derleyiciye NULL olmadığını vaat eder.


3

Burada "yasal" ne anlama geliyor? C standardı veya C ++ standardıyla çelişmeyen her şey bu standartlara göre yasaldır. Bir açıklama yaparsanız i = i++;ve sonuç olarak dinozorlar dünyayı ele geçirirse, bu standartlarla çelişmez. Ancak fizik kanunlarıyla çelişir, bu yüzden olmayacak :-)

Tanımlanmamış davranış derleyicinizi çökertirse, bu C veya C ++ standardını ihlal etmez. Ancak bu, derleyicinin kalitesinin iyileştirilebileceği (ve muhtemelen iyileştirilmesi gerektiği) anlamına gelir.

C standardının önceki sürümlerinde, hatalı olan veya tanımlanmamış davranışa bağlı olmayan ifadeler vardı:

char* p = 1 / 0;

Bir karaktere * 0 sabitinin atanmasına izin verilir. Sıfır olmayan bir sabite izin vermek değildir. 1/0 değeri tanımsız bir davranış olduğundan, derleyicinin bu ifadeyi kabul edip etmemesi tanımsız bir davranıştır. (Günümüzde 1/0 artık "tamsayı sabit ifadesi" tanımına uymuyor).


4
Kesin olmak gerekirse: dünyayı ele geçiren dinozorlar, herhangi bir fizik kanununa aykırı değildir (örneğin Jurassic Park varyasyonu). Bu pek olası değil. :)
freakish

-1

Standart, karşılaşırsa bir uygulamanın davranışına herhangi bir gereksinim getirmez #include "'foo'". Derleyici yazarı, belirtilen programı çıktısı geçici bir dosyaya yönlendirilmiş olarak çalıştırarak ve ardından #includeo dosyanın bir parçası gibi davranarak bu formun yönergelerini (dosya adı içinde kesme işaretlerini içeren) dahil etmenin yararlı olacağına karar verirse , Yukarıdaki satırı içeren bir programı işlemek foo, sonuç ne olursa olsun programı çalıştırabilir .

Bu nedenle, bir kişi onu çalıştırmak için hiç çaba sarf etmeseler bile, bir C programını çevirmeye çalışmanın bir sonucu olarak ne olabileceğine dair genel olarak bir sınır yoktur.


Herhangi bir programlama dilinde herhangi bir çevirmen veya derleyici için aynı şeyi söyleyebiliriz. Ya da bu konuda herhangi bir program.
Robert Harvey

@RobertHarvey: Birçok programlama dili özelliği bu tür şeyler hakkında çok daha spesifiktir. Bir dil spesifikasyonu, belirli bir direktifin, işletim sistemi yolu belirtilen bir akıştan girdiyi okuyacağını söylüyorsa ve işletim sistemi, belirli bir yolu okurken tuhaf bir şey yaparsa, bu, dil spesifikasyonunun kontrolü dışında olur, ancak ben Çoğu dil spesifikasyonunun, bu tür yönergeleri, davranışı başka türlü tanımlayacak platformlarda bile belgelemek zorunda kalmadan keyfi bir şekilde işlemesi için uygulamalara tam yetki vereceğini düşünüyorum.
supercat
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.